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 *err, gchar **err_info);
147 static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
148 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
149 static gboolean peekclassic_read_v56(wtap *wth, int *err, gchar **err_info,
150 gint64 *data_offset);
151 static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
152 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
153 static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
154 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
156 int peekclassic_open(wtap *wth, int *err, gchar **err_info)
158 peekclassic_header_t ep_hdr;
160 struct timeval reference_time;
162 peekclassic_t *peekclassic;
164 /* Peek classic files do not start with a magic value large enough
165 * to be unique; hence we use the following algorithm to determine
166 * the type of an unknown file:
167 * - populate the master header and reject file if there is no match
168 * - populate the secondary header and check that the reserved space
169 * is zero, and check some other fields; this isn't perfect,
170 * and we may have to add more checks at some point.
172 g_assert(sizeof(ep_hdr.master) == PEEKCLASSIC_MASTER_HDR_SIZE);
173 bytes_read = file_read(&ep_hdr.master, (int)sizeof(ep_hdr.master),
175 if (bytes_read != sizeof(ep_hdr.master)) {
176 *err = file_error(wth->fh, err_info);
177 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
183 * It appears that EtherHelp (a free application from WildPackets
184 * that did blind capture, saving to a file, so that you could
185 * give the resulting file to somebody with EtherPeek) saved
186 * captures in EtherPeek format except that it ORed the 0x80
187 * bit on in the version number.
189 * We therefore strip off the 0x80 bit in the version number.
190 * Perhaps there's some reason to care whether the capture
191 * came from EtherHelp; if we discover one, we should check
194 ep_hdr.master.version &= ~0x80;
196 /* switch on the file version */
197 switch (ep_hdr.master.version) {
202 /* get the secondary header */
203 g_assert(sizeof(ep_hdr.secondary.v567) ==
204 PEEKCLASSIC_V567_HDR_SIZE);
205 bytes_read = file_read(&ep_hdr.secondary.v567,
206 (int)sizeof(ep_hdr.secondary.v567), wth->fh);
207 if (bytes_read != sizeof(ep_hdr.secondary.v567)) {
208 *err = file_error(wth->fh, err_info);
209 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
214 if ((0 != ep_hdr.secondary.v567.reserved[0]) ||
215 (0 != ep_hdr.secondary.v567.reserved[1]) ||
216 (0 != ep_hdr.secondary.v567.reserved[2])) {
222 * Check the mediaType and physMedium fields.
223 * We assume it's not a Peek classic file if
224 * these aren't values we know, rather than
225 * reporting them as invalid Peek classic files,
226 * as, given the lack of a magic number, we need
227 * all the checks we can get.
229 ep_hdr.secondary.v567.mediaType =
230 g_ntohl(ep_hdr.secondary.v567.mediaType);
231 ep_hdr.secondary.v567.physMedium =
232 g_ntohl(ep_hdr.secondary.v567.physMedium);
234 switch (ep_hdr.secondary.v567.physMedium) {
238 * "Native" format, presumably meaning
239 * Ethernet or Token Ring.
241 switch (ep_hdr.secondary.v567.mediaType) {
244 file_encap = WTAP_ENCAP_ETHERNET;
248 file_encap = WTAP_ENCAP_TOKEN_RING;
253 * Assume this isn't a Peek classic file.
260 switch (ep_hdr.secondary.v567.mediaType) {
264 * 802.11, with a private header giving
265 * some radio information. Presumably
266 * this is from AiroPeek.
268 file_encap = WTAP_ENCAP_IEEE_802_11_AIROPEEK;
273 * Assume this isn't a Peek classic file.
281 * Assume this isn't a Peek classic file.
288 * Assume this is a V5, V6 or V7 Peek classic file, and
289 * byte swap the rest of the fields in the secondary header.
291 * XXX - we could check the file length if the file were
292 * uncompressed, but it might be compressed.
294 ep_hdr.secondary.v567.filelength =
295 g_ntohl(ep_hdr.secondary.v567.filelength);
296 ep_hdr.secondary.v567.numPackets =
297 g_ntohl(ep_hdr.secondary.v567.numPackets);
298 ep_hdr.secondary.v567.timeDate =
299 g_ntohl(ep_hdr.secondary.v567.timeDate);
300 ep_hdr.secondary.v567.timeStart =
301 g_ntohl(ep_hdr.secondary.v567.timeStart);
302 ep_hdr.secondary.v567.timeStop =
303 g_ntohl(ep_hdr.secondary.v567.timeStop);
304 ep_hdr.secondary.v567.appVers =
305 g_ntohl(ep_hdr.secondary.v567.appVers);
306 ep_hdr.secondary.v567.linkSpeed =
307 g_ntohl(ep_hdr.secondary.v567.linkSpeed);
309 /* Get the reference time as a "struct timeval" */
310 reference_time.tv_sec =
311 ep_hdr.secondary.v567.timeDate - mac2unix;
312 reference_time.tv_usec = 0;
317 * Assume this isn't a Peek classic file.
323 * This is a Peek classic file.
325 * At this point we have recognised the file type and have populated
326 * the whole ep_hdr structure in host byte order.
328 peekclassic = (peekclassic_t *)g_malloc(sizeof(peekclassic_t));
329 wth->priv = (void *)peekclassic;
330 peekclassic->reference_time = reference_time;
331 switch (ep_hdr.master.version) {
335 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PEEKCLASSIC_V56;
337 * XXX - can we get the file encapsulation from the
338 * header in the same way we do for V7 files?
340 wth->file_encap = WTAP_ENCAP_PER_PACKET;
341 wth->subtype_read = peekclassic_read_v56;
342 wth->subtype_seek_read = peekclassic_seek_read_v56;
346 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PEEKCLASSIC_V7;
347 wth->file_encap = file_encap;
348 wth->subtype_read = peekclassic_read_v7;
349 wth->subtype_seek_read = peekclassic_seek_read_v7;
353 /* this is impossible */
354 g_assert_not_reached();
357 wth->snapshot_length = 0; /* not available in header */
358 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
363 static gboolean peekclassic_read_v7(wtap *wth, int *err, gchar **err_info,
368 *data_offset = file_tell(wth->fh);
370 /* Read the packet. */
371 sliceLength = peekclassic_read_packet_v7(wth, wth->fh, &wth->phdr,
372 wth->frame_buffer, err, err_info);
376 /* Skip extra ignored data at the end of the packet. */
377 if ((guint32)sliceLength > wth->phdr.caplen) {
378 if (!file_skip(wth->fh, sliceLength - wth->phdr.caplen, err))
382 /* Records are padded to an even length, so if the slice length
383 is odd, read the padding byte. */
384 if (sliceLength & 0x01) {
385 if (!file_skip(wth->fh, 1, err))
392 static gboolean peekclassic_seek_read_v7(wtap *wth, gint64 seek_off,
393 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
395 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
398 /* Read the packet. */
399 if (peekclassic_read_packet_v7(wth, wth->random_fh, phdr, buf,
400 err, err_info) == -1) {
402 *err = WTAP_ERR_SHORT_READ;
408 static int peekclassic_read_packet_v7(wtap *wth, FILE_T fh,
409 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
411 guint8 ep_pkt[PEEKCLASSIC_V7_PKT_SIZE];
426 bytes_read = file_read(ep_pkt, sizeof(ep_pkt), fh);
427 if (bytes_read != (int) sizeof(ep_pkt)) {
428 *err = file_error(fh, err_info);
429 if (*err == 0 && bytes_read > 0)
430 *err = WTAP_ERR_SHORT_READ;
434 /* Extract the fields from the packet */
436 protoNum = pntoh16(&ep_pkt[PEEKCLASSIC_V7_PROTONUM_OFFSET]);
438 length = pntoh16(&ep_pkt[PEEKCLASSIC_V7_LENGTH_OFFSET]);
439 sliceLength = pntoh16(&ep_pkt[PEEKCLASSIC_V7_SLICE_LENGTH_OFFSET]);
441 flags = ep_pkt[PEEKCLASSIC_V7_FLAGS_OFFSET];
443 status = ep_pkt[PEEKCLASSIC_V7_STATUS_OFFSET];
444 timestamp = pntoh64(&ep_pkt[PEEKCLASSIC_V7_TIMESTAMP_OFFSET]);
446 /* force sliceLength to be the actual length of the packet */
447 if (0 == sliceLength) {
448 sliceLength = length;
451 /* fill in packet header values */
452 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
453 tsecs = (time_t) (timestamp/1000000);
454 tusecs = (guint32) (timestamp - tsecs*1000000);
455 phdr->ts.secs = tsecs - mac2unix;
456 phdr->ts.nsecs = tusecs * 1000;
458 phdr->caplen = sliceLength;
460 switch (wth->file_encap) {
462 case WTAP_ENCAP_IEEE_802_11_AIROPEEK:
463 phdr->pseudo_header.ieee_802_11.fcs_len = 0; /* no FCS */
464 phdr->pseudo_header.ieee_802_11.decrypted = FALSE;
467 * The last 4 bytes appear to be random data - the length
468 * might include the FCS - so we reduce the length by 4.
470 * Or maybe this is just the same kind of random 4 bytes
471 * of junk at the end you get in Wireless Sniffer
474 if (phdr->len < 4 || phdr->caplen < 4) {
475 *err = WTAP_ERR_BAD_FILE;
476 *err_info = g_strdup_printf("peekclassic: 802.11 packet has length < 4");
483 case WTAP_ENCAP_ETHERNET:
484 /* XXX - it appears that if the low-order bit of
485 "status" is 0, there's an FCS in this frame,
486 and if it's 1, there's 4 bytes of 0. */
487 phdr->pseudo_header.eth.fcs_len = (status & 0x01) ? 0 : 4;
491 /* read the packet data */
492 if (!wtap_read_packet_bytes(fh, buf, phdr->caplen, err, err_info))
498 static gboolean peekclassic_read_v56(wtap *wth, int *err, gchar **err_info,
501 *data_offset = file_tell(wth->fh);
503 /* read the packet */
504 if (!peekclassic_read_packet_v56(wth, wth->fh, &wth->phdr,
505 wth->frame_buffer, err, err_info))
509 * XXX - is the captured packet data padded to a multiple
515 static gboolean peekclassic_seek_read_v56(wtap *wth, gint64 seek_off,
516 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
518 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
521 /* read the packet */
522 if (!peekclassic_read_packet_v56(wth, wth->random_fh, phdr, buf,
525 *err = WTAP_ERR_SHORT_READ;
531 static gboolean peekclassic_read_packet_v56(wtap *wth, FILE_T fh,
532 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
534 peekclassic_t *peekclassic = (peekclassic_t *)wth->priv;
535 guint8 ep_pkt[PEEKCLASSIC_V56_PKT_SIZE];
553 wtap_file_read_expected_bytes(ep_pkt, sizeof(ep_pkt), fh, err,
556 /* Extract the fields from the packet */
557 length = pntoh16(&ep_pkt[PEEKCLASSIC_V56_LENGTH_OFFSET]);
558 sliceLength = pntoh16(&ep_pkt[PEEKCLASSIC_V56_SLICE_LENGTH_OFFSET]);
560 flags = ep_pkt[PEEKCLASSIC_V56_FLAGS_OFFSET];
561 status = ep_pkt[PEEKCLASSIC_V56_STATUS_OFFSET];
563 timestamp = pntoh32(&ep_pkt[PEEKCLASSIC_V56_TIMESTAMP_OFFSET]);
565 destNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_DESTNUM_OFFSET]);
566 srcNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_SRCNUM_OFFSET]);
568 protoNum = pntoh16(&ep_pkt[PEEKCLASSIC_V56_PROTONUM_OFFSET]);
570 memcpy(protoStr, &ep_pkt[PEEKCLASSIC_V56_PROTOSTR_OFFSET],
575 * XXX - is the captured packet data padded to a multiple
579 /* force sliceLength to be the actual length of the packet */
580 if (0 == sliceLength) {
581 sliceLength = length;
584 /* fill in packet header values */
585 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
586 /* timestamp is in milliseconds since reference_time */
587 phdr->ts.secs = peekclassic->reference_time.tv_sec
588 + (timestamp / 1000);
589 phdr->ts.nsecs = 1000 * (timestamp % 1000) * 1000;
591 phdr->caplen = sliceLength;
593 phdr->pkt_encap = WTAP_ENCAP_UNKNOWN;
594 for (i=0; i<NUM_PEEKCLASSIC_ENCAPS; i++) {
595 if (peekclassic_encap[i].protoNum == protoNum) {
596 phdr->pkt_encap = peekclassic_encap[i].encap;
600 switch (phdr->pkt_encap) {
602 case WTAP_ENCAP_ETHERNET:
603 /* We assume there's no FCS in this frame. */
604 phdr->pseudo_header.eth.fcs_len = 0;
608 /* read the packet data */
609 return wtap_read_packet_bytes(fh, buf, sliceLength, err, err_info);