2 * Routines for opening files in what WildPackets calls the tagged 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 2.0
9 * and later, EtherPeek 6.0 and later, EtherPeek NX 3.0 and later,
10 * EtherPeek VX 1.0 and later, GigaPeek NX 1.0 and later, Omni3 1.0
11 * and later (both OmniPeek and the Remote Engine), and WANPeek NX
12 * 1.0 and later. They also say it'll be used by future WildPackets
16 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
38 #include "file_wrappers.h"
39 #include <wsutil/buffer.h>
40 #include "peektagged.h"
44 * This file decoder could not have been writen without examining
45 * http://www.varsanofiev.com/inside/airopeekv9.htm, the help from
46 * Martin Regner and Guy Harris, and the etherpeek.c file (as it
47 * was called before renaming it to peekclassic.c).
53 * A Peek tagged file consists of multiple sections, each of which begins
54 * with a header in the following format.
56 * The section ID is a 4-character string saying what type of section
57 * it is. The section length is a little-endian field giving the
58 * length of the section, in bytes, including the section header
59 * itself. The other field of the section header is a little-endian
60 * constant that always appears to be 0x00000200.
62 * Files we've seen have the following sections, in order:
64 * "\177vers" - version information. The contents are XML, giving
65 * the file format version and application version information.
67 * "sess" - capture session information. The contents are XML, giving
68 * various information about the capture session.
70 * "pkts" - captured packets. The contents are binary records, one for
71 * each packet, with the record being a list of tagged values followed
72 * by the raw packet data.
74 typedef struct peektagged_section_header {
75 gint8 section_id[4]; /* string identifying the section */
76 guint32 section_len; /* little-endian section length */
77 guint32 section_const; /* little-endian 0x00000200 */
78 } peektagged_section_header_t;
81 * Network subtype values.
83 * XXX - do different network subtype values for 802.11 indicate different
84 * network adapter types, with some adapters supplying the FCS and others
85 * not supplying the FCS?
87 #define PEEKTAGGED_NST_ETHERNET 0
88 #define PEEKTAGGED_NST_802_11 1 /* 802.11 with 0's at the end */
89 #define PEEKTAGGED_NST_802_11_2 2 /* 802.11 with 0's at the end */
90 #define PEEKTAGGED_NST_802_11_WITH_FCS 3 /* 802.11 with FCS at the end */
92 /* tags for fields in packet header */
93 #define TAG_PEEKTAGGED_LENGTH 0x0000
94 #define TAG_PEEKTAGGED_TIMESTAMP_LOWER 0x0001
95 #define TAG_PEEKTAGGED_TIMESTAMP_UPPER 0x0002
96 #define TAG_PEEKTAGGED_FLAGS_AND_STATUS 0x0003
97 #define TAG_PEEKTAGGED_CHANNEL 0x0004
98 #define TAG_PEEKTAGGED_RATE 0x0005
99 #define TAG_PEEKTAGGED_SIGNAL_PERC 0x0006
100 #define TAG_PEEKTAGGED_SIGNAL_DBM 0x0007
101 #define TAG_PEEKTAGGED_NOISE_PERC 0x0008
102 #define TAG_PEEKTAGGED_NOISE_DBM 0x0009
103 #define TAG_PEEKTAGGED_UNKNOWN_0x000A 0x000A
104 #define TAG_PEEKTAGGED_UNKNOWN_0x000D 0x000D /* frequency? */
105 #define TAG_PEEKTAGGED_UNKNOWN_0x000E 0x000E
106 #define TAG_PEEKTAGGED_UNKNOWN_0x000F 0x000F /* 000F-0013 - dBm values? */
107 #define TAG_PEEKTAGGED_UNKNOWN_0x0010 0x0010
108 #define TAG_PEEKTAGGED_UNKNOWN_0x0011 0x0011
109 #define TAG_PEEKTAGGED_UNKNOWN_0x0012 0x0012
110 #define TAG_PEEKTAGGED_UNKNOWN_0x0013 0x0013
111 #define TAG_PEEKTAGGED_UNKNOWN_0x0014 0x0014
112 #define TAG_PEEKTAGGED_UNKNOWN_0x0015 0x0015
114 #define TAG_PEEKTAGGED_SLICE_LENGTH 0xffff
116 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
117 typedef struct peektagged_utime {
126 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
127 gint64 *data_offset);
128 static gboolean peektagged_seek_read(wtap *wth, gint64 seek_off,
129 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info);
131 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
140 c = file_getc(wth->fh);
143 *err = file_error(wth->fh, err_info);
144 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
145 return -1; /* error */
158 return (*cp == '\0' ? 1 : 0);
162 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
163 const char *separators, int *err,
170 for (cp = buffer, i = 0; i < buflen; i++, cp++)
172 c = file_getc(wth->fh);
175 *err = file_error(wth->fh, err_info);
176 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
177 return -1; /* error */
180 if (strchr (separators, c) != NULL)
192 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
200 ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
203 /* 0 means EOF, which means "not a valid Peek tagged file";
204 -1 means error, and "err" has been set. */
207 value = strtoul (str_num, &p, 10);
208 if (p == str_num || value > G_MAXUINT32)
210 *num = (guint32)value;
215 int peektagged_open(wtap *wth, int *err, gchar **err_info)
217 peektagged_section_header_t ap_hdr;
221 guint32 mediaSubType = 0;
223 static const int peektagged_encap[] = {
225 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
226 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
227 WTAP_ENCAP_IEEE_802_11_WITH_RADIO
229 #define NUM_PEEKTAGGED_ENCAPS (sizeof peektagged_encap / sizeof peektagged_encap[0])
230 peektagged_t *peektagged;
232 if (!wtap_read_bytes(wth->fh, &ap_hdr, (int)sizeof(ap_hdr), err, err_info)) {
233 if (*err != WTAP_ERR_SHORT_READ)
238 if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
239 return 0; /* doesn't begin with a "\177ver" section */
242 * XXX - we should get the length of the "\177ver" section, check
243 * that it's followed by a little-endian 0x00000200, and then,
244 * when reading the XML, make sure we don't go past the end of
245 * that section, and skip to the end of that section when
246 * we have the file version (and possibly check to make sure all
247 * tags are properly opened and closed).
249 ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
251 /* 0 means EOF, which means "not a valid Peek tagged file";
252 -1 means error, and "err" has been set. */
255 ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
257 /* 0 means EOF, which means "not a valid Peek tagged file";
258 -1 means error, and "err" has been set. */
262 /* If we got this far, we assume it's a Peek tagged file. */
263 if (fileVersion != 9) {
264 /* We only support version 9. */
265 *err = WTAP_ERR_UNSUPPORTED;
266 *err_info = g_strdup_printf("peektagged: version %u unsupported",
272 * XXX - once we've skipped the "\177ver" section, we should
273 * check for a "sess" section and fail if we don't see it.
274 * Then we should get the length of the "sess" section, check
275 * that it's followed by a little-endian 0x00000200, and then,
276 * when reading the XML, make sure we don't go past the end of
277 * that section, and skip to the end of the section when
278 * we have the file version (and possibly check to make sure all
279 * tags are properly opened and closed).
281 ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
285 *err = WTAP_ERR_BAD_FILE;
286 *err_info = g_strdup("peektagged: <MediaType> tag not found");
289 /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
290 files we've seen; should we require it to be 0? */
291 ret = wtap_file_read_number (wth, &mediaType, err, err_info);
295 *err = WTAP_ERR_BAD_FILE;
296 *err_info = g_strdup("peektagged: <MediaType> value not found");
300 ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
304 *err = WTAP_ERR_BAD_FILE;
305 *err_info = g_strdup("peektagged: <MediaSubType> tag not found");
308 ret = wtap_file_read_number (wth, &mediaSubType, err, err_info);
312 *err = WTAP_ERR_BAD_FILE;
313 *err_info = g_strdup("peektagged: <MediaSubType> value not found");
316 if (mediaSubType >= NUM_PEEKTAGGED_ENCAPS
317 || peektagged_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
318 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
319 *err_info = g_strdup_printf("peektagged: network type %u unknown or unsupported",
324 ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
328 *err = WTAP_ERR_SHORT_READ;
332 /* skip 8 zero bytes */
333 if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
337 * This is an Peek tagged file.
339 file_encap = peektagged_encap[mediaSubType];
341 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PEEKTAGGED;
342 wth->file_encap = file_encap;
343 wth->subtype_read = peektagged_read;
344 wth->subtype_seek_read = peektagged_seek_read;
345 wth->file_tsprec = WTAP_TSPREC_NSEC;
347 peektagged = (peektagged_t *)g_malloc(sizeof(peektagged_t));
348 wth->priv = (void *)peektagged;
349 switch (mediaSubType) {
351 case PEEKTAGGED_NST_ETHERNET:
352 case PEEKTAGGED_NST_802_11:
353 case PEEKTAGGED_NST_802_11_2:
354 peektagged->has_fcs = FALSE;
357 case PEEKTAGGED_NST_802_11_WITH_FCS:
358 peektagged->has_fcs = TRUE;
362 wth->snapshot_length = 0; /* not available in header */
370 peektagged_utime timestamp;
371 struct ieee_802_11_phdr ieee_802_11;
375 * Time stamps appear to be in nanoseconds since the Windows epoch
376 * as used in FILETIMEs, i.e. midnight, January 1, 1601.
378 * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
379 * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
380 * that are being subtracted.
382 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
387 * XXX - we should supply the additional radio information;
388 * the pseudo-header should probably be supplied in a fashion
389 * similar to the radiotap radio header, so that the 802.11
390 * dissector can determine which, if any, information items
394 peektagged_read_packet(wtap *wth, FILE_T fh, struct wtap_pkthdr *phdr,
395 Buffer *buf, int *err, gchar **err_info)
397 peektagged_t *peektagged = (peektagged_t *)wth->priv;
402 gboolean saw_length = FALSE;
403 gboolean saw_timestamp_lower = FALSE;
404 gboolean saw_timestamp_upper = FALSE;
408 memset(&hdr_info, 0, sizeof(hdr_info_t));
410 /* Extract the fields from the packet header */
412 /* Get the tag and value.
413 XXX - this assumes all values are 4 bytes long. */
414 if (!wtap_read_bytes_or_eof(fh, tag_value, sizeof tag_value, err, err_info)) {
417 * Short read if we've read something already;
418 * just an EOF if we haven't.
421 *err = WTAP_ERR_SHORT_READ;
425 header_len += (int) sizeof(tag_value);
426 tag = pletoh16(&tag_value[0]);
429 case TAG_PEEKTAGGED_LENGTH:
431 *err = WTAP_ERR_BAD_FILE;
432 *err_info = g_strdup("peektagged: record has two length fields");
435 hdr_info.length = pletoh32(&tag_value[2]);
439 case TAG_PEEKTAGGED_TIMESTAMP_LOWER:
440 if (saw_timestamp_lower) {
441 *err = WTAP_ERR_BAD_FILE;
442 *err_info = g_strdup("peektagged: record has two timestamp-lower fields");
445 hdr_info.timestamp.lower = pletoh32(&tag_value[2]);
446 saw_timestamp_lower = TRUE;
449 case TAG_PEEKTAGGED_TIMESTAMP_UPPER:
450 if (saw_timestamp_upper) {
451 *err = WTAP_ERR_BAD_FILE;
452 *err_info = g_strdup("peektagged: record has two timestamp-upper fields");
455 hdr_info.timestamp.upper = pletoh32(&tag_value[2]);
456 saw_timestamp_upper = TRUE;
459 case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
460 /* XXX - not used yet */
463 case TAG_PEEKTAGGED_CHANNEL:
464 hdr_info.ieee_802_11.channel = pletoh32(&tag_value[2]);
467 case TAG_PEEKTAGGED_RATE:
468 hdr_info.ieee_802_11.data_rate = pletoh32(&tag_value[2]);
471 case TAG_PEEKTAGGED_SIGNAL_PERC:
472 hdr_info.ieee_802_11.signal_level = pletoh32(&tag_value[2]);
475 case TAG_PEEKTAGGED_SIGNAL_DBM:
476 /* XXX - not used yet */
479 case TAG_PEEKTAGGED_NOISE_PERC:
480 /* XXX - not used yet */
483 case TAG_PEEKTAGGED_NOISE_DBM:
484 /* XXX - not used yet */
487 case TAG_PEEKTAGGED_UNKNOWN_0x000A:
488 /* XXX - seen in an OmniPeek 802.11n capture; value unknown */
491 case TAG_PEEKTAGGED_UNKNOWN_0x000D:
492 /* XXX - seen in an EtherPeek capture; value unknown */
493 /* XXX - seen in an AiroPeek/OmniPeek capture; frequency? */
496 case TAG_PEEKTAGGED_UNKNOWN_0x000E:
497 /* XXX - seen in an AiroPeek/OmniPeek capture; value unknown */
500 case TAG_PEEKTAGGED_UNKNOWN_0x000F:
501 /* XXX - seen in an AiroPeek/OmniPeek capture; dBm value? */
504 case TAG_PEEKTAGGED_UNKNOWN_0x0010:
505 /* XXX - seen in an AiroPeek/OmniPeek capture; dBm value? */
508 case TAG_PEEKTAGGED_UNKNOWN_0x0011:
509 /* XXX - seen in an AiroPeek/OmniPeek capture; dBm value? */
512 case TAG_PEEKTAGGED_UNKNOWN_0x0012:
513 /* XXX - seen in an AiroPeek/OmniPeek capture; dBm value? */
516 case TAG_PEEKTAGGED_UNKNOWN_0x0013:
517 /* XXX - seen in an AiroPeek/OmniPeek capture; dBm value? */
520 case TAG_PEEKTAGGED_UNKNOWN_0x0014:
521 /* XXX - seen in an AiroPeek/OmniPeek capture; value unknown */
524 case TAG_PEEKTAGGED_UNKNOWN_0x0015:
525 /* XXX - seen in an AiroPeek/OmniPeek capture; value unknown */
528 case TAG_PEEKTAGGED_SLICE_LENGTH:
529 hdr_info.sliceLength = pletoh32(&tag_value[2]);
535 } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH); /* last tag */
538 *err = WTAP_ERR_BAD_FILE;
539 *err_info = g_strdup("peektagged: record has no length field");
542 if (!saw_timestamp_lower) {
543 *err = WTAP_ERR_BAD_FILE;
544 *err_info = g_strdup("peektagged: record has no timestamp-lower field");
547 if (!saw_timestamp_upper) {
548 *err = WTAP_ERR_BAD_FILE;
549 *err_info = g_strdup("peektagged: record has no timestamp-upper field");
554 * If sliceLength is 0, force it to be the actual length of the packet.
556 if (hdr_info.sliceLength == 0)
557 hdr_info.sliceLength = hdr_info.length;
559 if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
561 * Probably a corrupt capture file; don't blow up trying
562 * to allocate space for an immensely-large packet.
564 *err = WTAP_ERR_BAD_FILE;
565 *err_info = g_strdup_printf("peektagged: File has %u-byte packet, bigger than maximum of %u",
566 hdr_info.sliceLength, WTAP_MAX_PACKET_SIZE);
570 phdr->rec_type = REC_TYPE_PACKET;
571 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
572 phdr->len = hdr_info.length;
573 phdr->caplen = hdr_info.sliceLength;
575 /* calculate and fill in packet time stamp */
576 t = (double) hdr_info.timestamp.lower +
577 (double) hdr_info.timestamp.upper * 4294967296.0;
579 t -= TIME_FIXUP_CONSTANT;
580 phdr->ts.secs = (time_t) t;
581 phdr->ts.nsecs = (guint32) ((t - phdr->ts.secs)*1000000000);
583 switch (wth->file_encap) {
585 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
586 phdr->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
587 if (peektagged->has_fcs)
588 phdr->pseudo_header.ieee_802_11.fcs_len = 4;
590 if (phdr->len < 4 || phdr->caplen < 4) {
591 *err = WTAP_ERR_BAD_FILE;
592 *err_info = g_strdup_printf("peektagged: 802.11 packet has length < 4");
595 phdr->pseudo_header.ieee_802_11.fcs_len = 0;
600 phdr->pseudo_header.ieee_802_11.decrypted = FALSE;
603 case WTAP_ENCAP_ETHERNET:
605 * The last 4 bytes appear to be 0 in the captures I've seen;
606 * are there any captures where it's an FCS?
608 if (phdr->len < 4 || phdr->caplen < 4) {
609 *err = WTAP_ERR_BAD_FILE;
610 *err_info = g_strdup_printf("peektagged: Ethernet packet has length < 4");
613 phdr->pseudo_header.eth.fcs_len = 0;
620 /* Read the packet data. */
621 if (!wtap_read_packet_bytes(fh, buf, phdr->caplen, err, err_info))
627 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
632 *data_offset = file_tell(wth->fh);
634 /* Read the packet. */
635 skip_len = peektagged_read_packet(wth, wth->fh, &wth->phdr,
636 wth->frame_buffer, err, err_info);
641 /* Skip extra junk at the end of the packet data. */
642 if (!file_skip(wth->fh, skip_len, err))
650 peektagged_seek_read(wtap *wth, gint64 seek_off,
651 struct wtap_pkthdr *phdr, Buffer *buf, int *err, gchar **err_info)
653 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
656 /* Read the packet. */
657 if (peektagged_read_packet(wth, wth->random_fh, phdr, buf, err, err_info) == -1) {
659 *err = WTAP_ERR_SHORT_READ;