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_subtype
= WTAP_FILE_TYPE_SUBTYPE_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_subtype
= WTAP_FILE_TYPE_SUBTYPE_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
);