2 * Routines for opening files in what WildPackets calls the classic file
3 * format in the description of their "PeekRdr Sample Application" (C++
4 * source code to read their capture files, downloading of which requires
5 * a maintenance contract, so it's not free as in beer and probably not
6 * as in speech, either).
8 * As that description says, it's used by AiroPeek and AiroPeek NX prior
9 * to 2.0, EtherPeek prior to 6.0, and EtherPeek NX prior to 3.0. It
10 * was probably also used by TokenPeek.
12 * This handles versions 5, 6, and 7 of that format (the format version
13 * number is what appears in the file, and is distinct from the application
16 * Copyright (c) 2001, Daniel Thompson <d.thompson@gmx.net>
21 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
23 * This program is free software; you can redistribute it and/or
24 * modify it under the terms of the GNU General Public License
25 * as published by the Free Software Foundation; either version 2
26 * of the License, or (at your option) any later version.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program; if not, write to the Free Software
35 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
42 #include "file_wrappers.h"
44 #include "peekclassic.h"
47 * This file decoder could not have been writen without examining how
48 * tcptrace (http://www.tcptrace.org/) handles EtherPeek files.
52 typedef struct peekclassic_master_header {
55 } peekclassic_master_header_t;
56 #define PEEKCLASSIC_MASTER_HDR_SIZE 2
58 /* secondary header (V5,V6,V7) */
59 typedef struct peekclassic_v567_header {
65 guint32 mediaType; /* Media Type Ethernet=0 Token Ring = 1 */
66 guint32 physMedium; /* Physical Medium native=0 802.1=1 */
67 guint32 appVers; /* App Version Number Maj.Min.Bug.Build */
68 guint32 linkSpeed; /* Link Speed Bits/sec */
70 } peekclassic_v567_header_t;
71 #define PEEKCLASSIC_V567_HDR_SIZE 48
74 typedef struct peekclassic_header {
75 peekclassic_master_header_t master;
77 peekclassic_v567_header_t v567;
79 } peekclassic_header_t;
82 * Packet header (V5, V6).
84 * NOTE: the time stamp, although it's a 32-bit number, is only aligned
85 * on a 16-bit boundary. (Does this date back to 68K Macs? The 68000
86 * only required 16-bit alignment of 32-bit quantities, as did the 68010,
87 * and the 68020/68030/68040 required no alignment.)
89 * As such, we cannot declare this as a C structure, as compilers on
90 * most platforms will put 2 bytes of padding before the time stamp to
91 * align it on a 32-bit boundary.
93 * So, instead, we #define numbers as the offsets of the fields.
95 #define PEEKCLASSIC_V56_LENGTH_OFFSET 0
96 #define PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET 2
97 #define PEEKCLASSIC_V56_FLAGS_OFFSET 4
98 #define PEEKCLASSIC_V56_STATUS_OFFSET 5
99 #define PEEKCLASSIC_V56_TIMESTAMP_OFFSET 6
100 #define PEEKCLASSIC_V56_DESTNUM_OFFSET 10
101 #define PEEKCLASSIC_V56_SRCNUM_OFFSET 12
102 #define PEEKCLASSIC_V56_PROTONUM_OFFSET 14
103 #define PEEKCLASSIC_V56_PROTOSTR_OFFSET 16
104 #define PEEKCLASSIC_V56_FILTERNUM_OFFSET 24
105 #define PEEKCLASSIC_V56_PKT_SIZE 26
107 /* 64-bit time in micro seconds from the (Mac) epoch */
108 typedef struct peekclassic_utime {
114 * Packet header (V7).
116 * This doesn't have the same alignment problem, but we do it with
119 #define PEEKCLASSIC_V7_PROTONUM_OFFSET 0
120 #define PEEKCLASSIC_V7_LENGTH_OFFSET 2
121 #define PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET 4
122 #define PEEKCLASSIC_V7_FLAGS_OFFSET 6
123 #define PEEKCLASSIC_V7_STATUS_OFFSET 7
124 #define PEEKCLASSIC_V7_TIMESTAMP_OFFSET 8
125 #define PEEKCLASSIC_V7_PKT_SIZE 16
127 typedef struct peekclassic_encap_lookup {
130 } peekclassic_encap_lookup_t;
132 static const unsigned int mac2unix = 2082844800u;
133 static const peekclassic_encap_lookup_t peekclassic_encap[] = {
134 { 1400, WTAP_ENCAP_ETHERNET }
136 #define NUM_PEEKCLASSIC_ENCAPS \
137 (sizeof (peekclassic_encap) / sizeof (peekclassic_encap[0]))
140 struct timeval reference_time;
143 static gboolean peekclassic_read_v7(wtap *wth, int *err, gchar **err_info,
144 gint64 *data_offset);
145 static gboolean peekclassic_seek_read_v7(wtap *wth, gint64 seek_off,
146 struct wtap_pkthdr *phdr, Buffer *buf, int length,
147 int *err, gchar **err_info);
148 static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
149 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
150 static gboolean peekclassic_read_v56(wtap *wth, int *err, gchar **err_info,
151 gint64 *data_offset);
152 static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
153 struct wtap_pkthdr *phdr, Buffer *buf, int length,
154 int *err, gchar **err_info);
155 static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
156 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
158 int peekclassic_open(wtap *wth, int *err, gchar **err_info)
160 peekclassic_header_t ep_hdr;
162 struct timeval reference_time;
164 peekclassic_t *peekclassic;
166 /* Peek classic files do not start with a magic value large enough
167 * to be unique; hence we use the following algorithm to determine
168 * the type of an unknown file:
169 * - populate the master header and reject file if there is no match
170 * - populate the secondary header and check that the reserved space
171 * is zero, and check some other fields; this isn't perfect,
172 * and we may have to add more checks at some point.
174 g_assert(sizeof(ep_hdr.master) == PEEKCLASSIC_MASTER_HDR_SIZE);
175 bytes_read = file_read(&ep_hdr.master, (int)sizeof(ep_hdr.master),
177 if (bytes_read != sizeof(ep_hdr.master)) {
178 *err = file_error(wth->fh, err_info);
179 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
185 * It appears that EtherHelp (a free application from WildPackets
186 * that did blind capture, saving to a file, so that you could
187 * give the resulting file to somebody with EtherPeek) saved
188 * captures in EtherPeek format except that it ORed the 0x80
189 * bit on in the version number.
191 * We therefore strip off the 0x80 bit in the version number.
192 * Perhaps there's some reason to care whether the capture
193 * came from EtherHelp; if we discover one, we should check
196 ep_hdr.master.version &= ~0x80;
198 /* switch on the file version */
199 switch (ep_hdr.master.version) {
204 /* get the secondary header */
205 g_assert(sizeof(ep_hdr.secondary.v567) ==
206 PEEKCLASSIC_V567_HDR_SIZE);
207 bytes_read = file_read(&ep_hdr.secondary.v567,
208 (int)sizeof(ep_hdr.secondary.v567), wth->fh);
209 if (bytes_read != sizeof(ep_hdr.secondary.v567)) {
210 *err = file_error(wth->fh, err_info);
211 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
216 if ((0 != ep_hdr.secondary.v567.reserved[0]) ||
217 (0 != ep_hdr.secondary.v567.reserved[1]) ||
218 (0 != ep_hdr.secondary.v567.reserved[2])) {
224 * Check the mediaType and physMedium fields.
225 * We assume it's not a Peek classic file if
226 * these aren't values we know, rather than
227 * reporting them as invalid Peek classic files,
228 * as, given the lack of a magic number, we need
229 * all the checks we can get.
231 ep_hdr.secondary.v567.mediaType =
232 g_ntohl(ep_hdr.secondary.v567.mediaType);
233 ep_hdr.secondary.v567.physMedium =
234 g_ntohl(ep_hdr.secondary.v567.physMedium);
236 switch (ep_hdr.secondary.v567.physMedium) {
240 * "Native" format, presumably meaning
241 * Ethernet or Token Ring.
243 switch (ep_hdr.secondary.v567.mediaType) {
246 file_encap = WTAP_ENCAP_ETHERNET;
250 file_encap = WTAP_ENCAP_TOKEN_RING;
255 * Assume this isn't a Peek classic file.
262 switch (ep_hdr.secondary.v567.mediaType) {
266 * 802.11, with a private header giving
267 * some radio information. Presumably
268 * this is from AiroPeek.
270 file_encap = WTAP_ENCAP_IEEE_802_11_AIROPEEK;
275 * Assume this isn't a Peek classic file.
283 * Assume this isn't a Peek classic file.
290 * Assume this is a V5, V6 or V7 Peek classic file, and
291 * byte swap the rest of the fields in the secondary header.
293 * XXX - we could check the file length if the file were
294 * uncompressed, but it might be compressed.
296 ep_hdr.secondary.v567.filelength =
297 g_ntohl(ep_hdr.secondary.v567.filelength);
298 ep_hdr.secondary.v567.numPackets =
299 g_ntohl(ep_hdr.secondary.v567.numPackets);
300 ep_hdr.secondary.v567.timeDate =
301 g_ntohl(ep_hdr.secondary.v567.timeDate);
302 ep_hdr.secondary.v567.timeStart =
303 g_ntohl(ep_hdr.secondary.v567.timeStart);
304 ep_hdr.secondary.v567.timeStop =
305 g_ntohl(ep_hdr.secondary.v567.timeStop);
306 ep_hdr.secondary.v567.appVers =
307 g_ntohl(ep_hdr.secondary.v567.appVers);
308 ep_hdr.secondary.v567.linkSpeed =
309 g_ntohl(ep_hdr.secondary.v567.linkSpeed);
311 /* Get the reference time as a "struct timeval" */
312 reference_time.tv_sec =
313 ep_hdr.secondary.v567.timeDate - mac2unix;
314 reference_time.tv_usec = 0;
319 * Assume this isn't a Peek classic file.
325 * This is a Peek classic file.
327 * At this point we have recognised the file type and have populated
328 * the whole ep_hdr structure in host byte order.
330 peekclassic = (peekclassic_t *)g_malloc(sizeof(peekclassic_t));
331 wth->priv = (void *)peekclassic;
332 peekclassic->reference_time = reference_time;
333 switch (ep_hdr.master.version) {
337 wth->file_type = WTAP_FILE_PEEKCLASSIC_V56;
339 * XXX - can we get the file encapsulation from the
340 * header in the same way we do for V7 files?
342 wth->file_encap = WTAP_ENCAP_PER_PACKET;
343 wth->subtype_read = peekclassic_read_v56;
344 wth->subtype_seek_read = peekclassic_seek_read_v56;
348 wth->file_type = WTAP_FILE_PEEKCLASSIC_V7;
349 wth->file_encap = file_encap;
350 wth->subtype_read = peekclassic_read_v7;
351 wth->subtype_seek_read = peekclassic_seek_read_v7;
355 /* this is impossible */
356 g_assert_not_reached();
359 wth->snapshot_length = 0; /* not available in header */
360 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
365 static gboolean peekclassic_read_v7(wtap *wth, int *err, gchar **err_info,
370 *data_offset = file_tell(wth->fh);
372 /* Read the packet. */
373 sliceLength = peekclassic_read_packet_v7(wth, wth->fh, &wth->phdr,
374 wth->frame_buffer, err, err_info);
378 /* Skip extra ignored data at the end of the packet. */
379 if ((guint32)sliceLength > wth->phdr.caplen) {
380 if (!file_skip(wth->fh, sliceLength - wth->phdr.caplen, err))
384 /* Records are padded to an even length, so if the slice length
385 is odd, read the padding byte. */
386 if (sliceLength & 0x01) {
387 if (!file_skip(wth->fh, 1, err))
394 static gboolean peekclassic_seek_read_v7(wtap *wth, gint64 seek_off,
395 struct wtap_pkthdr *phdr, Buffer *buf, int length _U_,
396 int *err, gchar **err_info)
398 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
401 /* Read the packet. */
402 if (peekclassic_read_packet_v7(wth, wth->random_fh, phdr, buf,
403 err, err_info) == -1) {
405 *err = WTAP_ERR_SHORT_READ;
411 static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
412 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
414 guint8 ep_pkt[PEEKCLASSIC_V7_PKT_SIZE];
429 bytes_read = file_read(ep_pkt, sizeof(ep_pkt), fh);
430 if (bytes_read != (int) sizeof(ep_pkt)) {
431 *err = file_error(fh, err_info);
432 if (*err == 0 && bytes_read > 0)
433 *err = WTAP_ERR_SHORT_READ;
437 /* Extract the fields from the packet */
439 protoNum = pntohs(&ep_pkt[PEEKCLASSIC_V7_PROTONUM_OFFSET]);
441 length = pntohs(&ep_pkt[PEEKCLASSIC_V7_LENGTH_OFFSET]);
442 sliceLength = pntohs(&ep_pkt[PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET]);
444 flags = ep_pkt[PEEKCLASSIC_V7_FLAGS_OFFSET];
446 status = ep_pkt[PEEKCLASSIC_V7_STATUS_OFFSET];
447 timestamp = pntohll(&ep_pkt[PEEKCLASSIC_V7_TIMESTAMP_OFFSET]);
449 /* force sliceLength to be the actual length of the packet */
450 if (0 == sliceLength) {
451 sliceLength = length;
454 /* fill in packet header values */
455 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
456 tsecs = (time_t) (timestamp/1000000);
457 tusecs = (guint32) (timestamp - tsecs*1000000);
458 phdr->ts.secs = tsecs - mac2unix;
459 phdr->ts.nsecs = tusecs * 1000;
461 phdr->caplen = sliceLength;
463 switch (wth->file_encap) {
465 case WTAP_ENCAP_IEEE_802_11_AIROPEEK:
466 phdr->pseudo_header.ieee_802_11.fcs_len = 0; /* no FCS */
467 phdr->pseudo_header.ieee_802_11.decrypted = FALSE;
470 * The last 4 bytes appear to be random data - the length
471 * might include the FCS - so we reduce the length by 4.
473 * Or maybe this is just the same kind of random 4 bytes
474 * of junk at the end you get in Wireless Sniffer
477 if (phdr->len < 4 || phdr->caplen < 4) {
478 *err = WTAP_ERR_BAD_FILE;
479 *err_info = g_strdup_printf("peekclassic: 802.11 packet has length < 4");
486 case WTAP_ENCAP_ETHERNET:
487 /* XXX - it appears that if the low-order bit of
488 "status" is 0, there's an FCS in this frame,
489 and if it's 1, there's 4 bytes of 0. */
490 phdr->pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4;
494 /* read the packet data */
495 if (!wtap_read_packet_bytes(fh, buf, phdr->caplen, err, err_info))
501 static gboolean peekclassic_read_v56(wtap *wth, int *err, gchar **err_info,
504 *data_offset = file_tell(wth->fh);
506 /* read the packet */
507 if (!peekclassic_read_packet_v56(wth, wth->fh, &wth->phdr,
508 wth->frame_buffer, err, err_info))
512 * XXX - is the captured packet data padded to a multiple
518 static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
519 struct wtap_pkthdr *phdr, Buffer *buf, int length _U_,
520 int *err, gchar **err_info)
522 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
525 /* read the packet */
526 if (!peekclassic_read_packet_v56(wth, wth->random_fh, phdr, buf,
529 *err = WTAP_ERR_SHORT_READ;
535 static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
536 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
538 peekclassic_t *peekclassic = (peekclassic_t *)wth->priv;
539 guint8 ep_pkt[PEEKCLASSIC_V56_PKT_SIZE];
557 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), fh, err,
560 /* Extract the fields from the packet */
561 length = pntohs(&ep_pkt[PEEKCLASSIC_V56_LENGTH_OFFSET]);
562 sliceLength = pntohs(&ep_pkt[PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET]);
564 flags = ep_pkt[PEEKCLASSIC_V56_FLAGS_OFFSET];
565 status = ep_pkt[PEEKCLASSIC_V56_STATUS_OFFSET];
567 timestamp = pntohl(&ep_pkt[PEEKCLASSIC_V56_TIMESTAMP_OFFSET]);
569 destNum = pntohs(&ep_pkt[PEEKCLASSIC_V56_DESTNUM_OFFSET]);
570 srcNum = pntohs(&ep_pkt[PEEKCLASSIC_V56_SRCNUM_OFFSET]);
572 protoNum = pntohs(&ep_pkt[PEEKCLASSIC_V56_PROTONUM_OFFSET]);
574 memcpy(protoStr, &ep_pkt[PEEKCLASSIC_V56_PROTOSTR_OFFSET],
579 * XXX - is the captured packet data padded to a multiple
583 /* force sliceLength to be the actual length of the packet */
584 if (0 == sliceLength) {
585 sliceLength = length;
588 /* fill in packet header values */
589 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
590 /* timestamp is in milliseconds since reference_time */
591 phdr->ts.secs = peekclassic->reference_time.tv_sec
592 + (timestamp / 1000);
593 phdr->ts.nsecs = 1000 * (timestamp % 1000) * 1000;
595 phdr->caplen = sliceLength;
597 phdr->pkt_encap = WTAP_ENCAP_UNKNOWN;
598 for (i=0; i<NUM_PEEKCLASSIC_ENCAPS; i++) {
599 if (peekclassic_encap[i].protoNum == protoNum) {
600 phdr->pkt_encap = peekclassic_encap[i].encap;
604 switch (phdr->pkt_encap) {
606 case WTAP_ENCAP_ETHERNET:
607 /* We assume there's no FCS in this frame. */
608 phdr->pseudo_header.eth.fcs_len = 0;
612 /* read the packet data */
613 return wtap_read_packet_bytes(fh, buf, sliceLength, err, err_info);