Squelch an MSVC++ complaint.
[obnox/wireshark/wip.git] / wiretap / etherpeek.c
1 /* etherpeek.c
2  * Routines for opening etherpeek files
3  * Copyright (c) 2001, Daniel Thompson <d.thompson@gmx.net>
4  *
5  * $Id: etherpeek.c,v 1.5 2001/10/04 08:30:35 guy Exp $
6  *
7  * Wiretap Library
8  * Copyright (c) 1998 by Gilbert Ramirez <gram@xiexie.org>
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  *
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 #include <errno.h>
30 #include <string.h>
31 #include "wtap-int.h"
32 #include "file_wrappers.h"
33 #include "buffer.h"
34 #include "etherpeek.h"
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38
39 /* CREDITS
40  *
41  * This file decoder could not have been writen without examining how
42  * tcptrace (http://www.tcptrace.org/) handles etherpeek files.
43  */
44
45 /* master header */
46 typedef struct etherpeek_master_header {
47         guint8  version;
48         guint8  status;
49 } etherpeek_master_header_t;
50 #define ETHERPEEK_MASTER_HDR_SIZE 2
51
52 /* secondary header (Mac V5,V6,V7) */
53 typedef struct etherpeek_m567_header {
54         guint32 filelength;
55         guint32 numPackets;
56         guint32 timeDate;
57         guint32 timeStart;
58         guint32 timeStop;
59         guint32 reserved[7];
60 } etherpeek_m567_header_t;
61 #define ETHERPEEK_M567_HDR_SIZE 48
62
63 /* full header */
64 typedef struct etherpeek_header {
65         etherpeek_master_header_t master;
66         union {
67                 etherpeek_m567_header_t m567;
68         } secondary;
69 } etherpeek_header_t;
70
71 /* packet header (Mac V5, V6) */
72 typedef struct etherpeek_m56_packet {
73         guint16 length;
74         guint16 sliceLength;
75         guint8  flags;
76         guint8  status;
77         guint32 timestamp;
78         guint16 destNum;
79         guint16 srcNum;
80         guint16 protoNum;
81         char    protoStr[8];
82 } etherpeek_m56_packet_t;
83 #define ETHERPEEK_M56_PKT_SIZE 24
84
85 /* 64-bit time in micro seconds from the (Mac) epoch */
86 typedef struct etherpeek_utime {
87         guint32 upper;
88         guint32 lower;
89 } etherpeek_utime;
90
91 /* packet header (Mac V7) */
92 typedef struct etherpeek_m7_packet {
93         guint16 protoNum;
94         guint16 length;
95         guint16 sliceLength;
96         guint8  flags;
97         guint8  status;
98         etherpeek_utime
99                 timestamp;
100 } etherpeek_m7_packet_t;
101 #define ETHERPEEK_M7_PKT_SIZE 16
102
103 typedef struct etherpeek_encap_lookup {
104         guint16 protoNum;
105         int     encap;
106 } etherpeek_encap_lookup_t;
107
108 static const unsigned int mac2unix = 2082844800u;
109 static const etherpeek_encap_lookup_t etherpeek_encap[] = {
110         { 1400, WTAP_ENCAP_ETHERNET }
111 };
112 #define NUM_ETHERPEEK_ENCAPS \
113         (sizeof (etherpeek_encap) / sizeof (etherpeek_encap[0]))
114
115 static gboolean etherpeek_read_m7(wtap *wth, int *err, long *data_offset);
116 static gboolean etherpeek_read_m56(wtap *wth, int *err, long *data_offset);
117
118 int etherpeek_open(wtap *wth, int *err)
119 {
120         etherpeek_header_t ep_hdr;
121
122         /* etherpeek files to not start with a magic value large enough
123          * to be unique hence we use the following algorithm to determine
124          * the type of an unknown file
125          *  - populate the master header and reject file if there is no match
126          *  - populate the secondary header and check that the reserved space
127          *      is zero; there is an obvious flaw here so this algorithm will
128          *      probably need to be revisiting when improving etherpeek
129          *      support
130          */
131         
132         g_assert(sizeof(ep_hdr.master) == ETHERPEEK_MASTER_HDR_SIZE);
133         wtap_file_read_unknown_bytes(
134                 &ep_hdr.master, sizeof(ep_hdr.master), wth->fh, err);
135         wth->data_offset += sizeof(ep_hdr.master);
136
137         /* switch on the file version */
138         switch (ep_hdr.master.version) {
139                 case 5:
140                 case 6:
141                 case 7:
142                         /* get the secondary header */
143                         g_assert(sizeof(ep_hdr.secondary.m567) ==
144                                 ETHERPEEK_M567_HDR_SIZE);
145                         wtap_file_read_unknown_bytes(
146                                 &ep_hdr.secondary.m567,
147                                 sizeof(ep_hdr.secondary.m567), wth->fh, err);
148                         wth->data_offset += sizeof(ep_hdr.secondary.m567);
149                         
150                         if ((0 != ep_hdr.secondary.m567.reserved[0]) ||
151                             (0 != ep_hdr.secondary.m567.reserved[1]) ||
152                             (0 != ep_hdr.secondary.m567.reserved[2]) ||
153                             (0 != ep_hdr.secondary.m567.reserved[3])) {
154                                 /* still unknown */
155                                 return 0;
156                         }
157
158                         /* we have a match for a Mac V5, V6 or V7,
159                          * so it is worth preforming byte swaps
160                          */
161                         ep_hdr.secondary.m567.filelength =
162                                 ntohl(ep_hdr.secondary.m567.filelength);
163                         ep_hdr.secondary.m567.numPackets =
164                                 ntohl(ep_hdr.secondary.m567.numPackets);
165                         ep_hdr.secondary.m567.timeDate =
166                                 ntohl(ep_hdr.secondary.m567.timeDate);
167                         ep_hdr.secondary.m567.timeStart =
168                                 ntohl(ep_hdr.secondary.m567.timeStart);
169                         ep_hdr.secondary.m567.timeStop =
170                                 ntohl(ep_hdr.secondary.m567.timeStop);
171
172                         /* populate the pseudo header */
173                         wth->pseudo_header.etherpeek.reference_time.tv_sec  =
174                                 ep_hdr.secondary.m567.timeDate - mac2unix;
175                         wth->pseudo_header.etherpeek.reference_time.tv_usec =
176                                 0;
177                         break;
178                 default:
179                         return 0;
180         }
181
182         /* at this point we have recognised the file type and have populated
183          * the whole ep_hdr structure in host byte order
184          */
185         
186         switch (ep_hdr.master.version) {
187                 case 5:
188                 case 6:
189                         wth->file_type = WTAP_FILE_ETHERPEEK_MAC_V56;
190                         wth->subtype_read = etherpeek_read_m56;
191                         wth->subtype_seek_read = wtap_def_seek_read;
192                         break;
193                 case 7:
194                         wth->file_type = WTAP_FILE_ETHERPEEK_MAC_V7;
195                         wth->subtype_read = etherpeek_read_m7;
196                         wth->subtype_seek_read = wtap_def_seek_read;
197                         break;
198                 default:
199                         /* this is impossible */
200                         g_assert_not_reached();
201         };
202
203         wth->file_encap        = WTAP_ENCAP_PER_PACKET;
204         wth->snapshot_length   = 16384; /* just guessing */
205
206         return 1;
207 }
208
209 static gboolean etherpeek_read_m7(wtap *wth, int *err, long *data_offset)
210 {
211         etherpeek_m7_packet_t ep_pkt;
212         double  t;
213         unsigned int i;
214
215         g_assert(sizeof(ep_pkt) == ETHERPEEK_M7_PKT_SIZE);
216         wtap_file_read_expected_bytes(&ep_pkt, sizeof(ep_pkt), wth->fh, err);
217         wth->data_offset += sizeof(ep_pkt);
218
219         /* byte swaps */
220         ep_pkt.protoNum = ntohs(ep_pkt.protoNum);
221         ep_pkt.length = ntohs(ep_pkt.length);
222         ep_pkt.sliceLength = ntohs(ep_pkt.sliceLength);
223         ep_pkt.timestamp.upper = ntohl(ep_pkt.timestamp.upper);
224         ep_pkt.timestamp.lower = ntohl(ep_pkt.timestamp.lower);
225
226         /* force sliceLength to be the actual length of the packet */
227         if (0 == ep_pkt.sliceLength) {
228                 ep_pkt.sliceLength = ep_pkt.length;
229         }
230
231         /* test for corrupt data */
232         if (ep_pkt.sliceLength > WTAP_MAX_PACKET_SIZE) {
233                 *err = WTAP_ERR_BAD_RECORD;
234                 return FALSE;
235         }
236
237         *data_offset = wth->data_offset;
238
239         /* read the frame data */
240         buffer_assure_space(wth->frame_buffer, ep_pkt.sliceLength);
241         wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
242                                       ep_pkt.sliceLength, wth->fh, err);
243         wth->data_offset += ep_pkt.sliceLength;
244         
245         /* fill in packet header values */
246         wth->phdr.len    = ep_pkt.length;
247         wth->phdr.caplen = ep_pkt.sliceLength;
248         
249         t =  (double) ep_pkt.timestamp.lower +
250              (double) ep_pkt.timestamp.upper * 4294967296.0;
251         t -= (double) mac2unix * 1000000.0;
252         wth->phdr.ts.tv_sec  = (time_t)  (t/1000000.0);
253         wth->phdr.ts.tv_usec = (guint32) (t - (double) wth->phdr.ts.tv_sec *
254                                                        1000000.0);
255         wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN;
256         for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
257                 if (etherpeek_encap[i].protoNum == ep_pkt.protoNum) {
258                         wth->phdr.pkt_encap = etherpeek_encap[i].encap;
259                 }
260         }
261
262         return TRUE;
263 }
264
265 static gboolean etherpeek_read_m56(wtap *wth, int *err, long *data_offset)
266 {
267         etherpeek_m56_packet_t ep_pkt;
268         unsigned int i;
269
270         g_assert(sizeof(ep_pkt) == ETHERPEEK_M56_PKT_SIZE);
271         wtap_file_read_expected_bytes(&ep_pkt, sizeof(ep_pkt), wth->fh, err);
272         wth->data_offset += sizeof(ep_pkt);
273
274         /* byte swaps */
275         ep_pkt.length = ntohs(ep_pkt.length);
276         ep_pkt.sliceLength = ntohs(ep_pkt.sliceLength);
277         ep_pkt.timestamp = ntohl(ep_pkt.timestamp);
278         ep_pkt.destNum = ntohs(ep_pkt.destNum);
279         ep_pkt.srcNum = ntohs(ep_pkt.srcNum);
280         ep_pkt.protoNum = ntohs(ep_pkt.protoNum);
281
282         /* force sliceLength to be the actual length of the packet */
283         if (0 == ep_pkt.sliceLength) {
284                 ep_pkt.sliceLength = ep_pkt.length;
285         }
286
287         /* test for corrupt data */
288         if (ep_pkt.sliceLength > WTAP_MAX_PACKET_SIZE) {
289                 *err = WTAP_ERR_BAD_RECORD;
290                 return FALSE;
291         }
292
293         *data_offset = wth->data_offset;
294
295         /* fill in packet header values */
296         wth->phdr.len        = ep_pkt.length;
297         wth->phdr.caplen     = ep_pkt.sliceLength;
298         /* timestamp is in milliseconds since reference_time */
299         wth->phdr.ts.tv_sec  = wth->pseudo_header.etherpeek.
300                 reference_time.tv_sec + (ep_pkt.timestamp / 1000);
301         wth->phdr.ts.tv_usec = 1000 * (ep_pkt.timestamp % 1000);
302         
303         wth->phdr.pkt_encap = WTAP_ENCAP_UNKNOWN;
304         for (i=0; i<NUM_ETHERPEEK_ENCAPS; i++) {
305                 if (etherpeek_encap[i].protoNum == ep_pkt.protoNum) {
306                         wth->phdr.pkt_encap = etherpeek_encap[i].encap;
307                 }
308         }
309
310         return TRUE;
311 }