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
18 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
20 * This program is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU General Public License
22 * as published by the Free Software Foundation; either version 2
23 * of the License, or (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
40 #include "file_wrappers.h"
42 #include "peektagged.h"
46 * This file decoder could not have been writen without examining
47 * http://www.varsanofiev.com/inside/peektagged.htm, the help from
48 * Martin Regner and Guy Harris, and the etherpeek.c file (as it
49 * was called before renaming it to peekclassic.c).
53 typedef struct peektagged_section_header {
56 guint32 section_const;
57 } peektagged_section_header_t;
60 * Network subtype values.
62 * XXX - do different network subtype values for 802.11 indicate different
63 * network adapter types, with some adapters supplying the FCS and others
64 * not supplying the FCS?
66 #define PEEKTAGGED_NST_ETHERNET 0
67 #define PEEKTAGGED_NST_802_11 1 /* 802.11 with 0's at the end */
68 #define PEEKTAGGED_NST_802_11_2 2 /* 802.11 with 0's at the end */
69 #define PEEKTAGGED_NST_802_11_WITH_FCS 3 /* 802.11 with FCS at the end */
71 /* tags for fields in packet header */
72 #define TAG_PEEKTAGGED_LENGTH 0x0000
73 #define TAG_PEEKTAGGED_TIMESTAMP_LOWER 0x0001
74 #define TAG_PEEKTAGGED_TIMESTAMP_UPPER 0x0002
75 #define TAG_PEEKTAGGED_FLAGS_AND_STATUS 0x0003
76 #define TAG_PEEKTAGGED_CHANNEL 0x0004
77 #define TAG_PEEKTAGGED_RATE 0x0005
78 #define TAG_PEEKTAGGED_SIGNAL_PERC 0x0006
79 #define TAG_PEEKTAGGED_SIGNAL_DBM 0x0007
80 #define TAG_PEEKTAGGED_NOISE_PERC 0x0008
81 #define TAG_PEEKTAGGED_NOISE_DBM 0x0009
82 #define TAG_PEEKTAGGED_UNKNOWN_0x000D 0x000D
83 #define TAG_PEEKTAGGED_SLICE_LENGTH 0xffff
85 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
86 typedef struct peektagged_utime {
95 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
97 static gboolean peektagged_seek_read(wtap *wth, gint64 seek_off,
98 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
99 int *err, gchar **err_info);
101 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
110 c = file_getc(wth->fh);
112 if (file_eof(wth->fh))
115 *err = file_error(wth->fh, err_info);
116 return -1; /* error */
129 return (*cp == '\0' ? 1 : 0);
133 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
134 const char *separators, int *err,
141 for (cp = buffer, i = 0; i < buflen; i++, cp++)
143 c = file_getc(wth->fh);
145 if (file_eof(wth->fh))
148 *err = file_error(wth->fh, err_info);
149 return -1; /* error */
152 if (strchr (separators, c) != NULL)
164 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
172 ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
175 /* 0 means EOF, which means "not a valid Peek tagged file";
176 -1 means error, and "err" has been set. */
179 value = strtoul (str_num, &p, 10);
180 if (p == str_num || value > G_MAXUINT32)
182 *num = (guint32)value;
187 int peektagged_open(wtap *wth, int *err, gchar **err_info)
189 peektagged_section_header_t ap_hdr;
193 guint32 mediaSubType = 0;
195 static const int peektagged_encap[] = {
197 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
198 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
199 WTAP_ENCAP_IEEE_802_11_WITH_RADIO
201 #define NUM_PEEKTAGGED_ENCAPS (sizeof peektagged_encap / sizeof peektagged_encap[0])
202 peektagged_t *peektagged;
204 wtap_file_read_unknown_bytes(&ap_hdr, sizeof(ap_hdr), wth->fh, err,
207 if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
208 return 0; /* doesn't begin with a "\177ver" section */
211 * XXX - we should get the length of the "\177ver" section, check
212 * that it's followed by a little-endian 0x00000200, and then,
213 * when reading the XML, make sure we don't go past the end of
214 * that section, and skip to the end of that section when
215 * we have the file version (and possibly check to make sure all
216 * tags are properly opened and closed).
218 ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
220 /* 0 means EOF, which means "not a valid Peek tagged file";
221 -1 means error, and "err" has been set. */
224 ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
226 /* 0 means EOF, which means "not a valid Peek tagged file";
227 -1 means error, and "err" has been set. */
231 /* If we got this far, we assume it's a Peek tagged file. */
232 if (fileVersion != 9) {
233 /* We only support version 9. */
234 *err = WTAP_ERR_UNSUPPORTED;
235 *err_info = g_strdup_printf("peektagged: version %u unsupported",
241 * XXX - once we've skipped the "\177ver" section, we should
242 * check for a "sess" section and fail if we don't see it.
243 * Then we should get the length of the "sess" section, check
244 * that it's followed by a little-endian 0x00000200, and then,
245 * when reading the XML, make sure we don't go past the end of
246 * that section, and skip to the end of the section when
247 * we have the file version (and possibly check to make sure all
248 * tags are properly opened and closed).
250 ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
254 *err = WTAP_ERR_BAD_FILE;
255 *err_info = g_strdup("peektagged: <MediaType> tag not found");
258 /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
259 files we've seen; should we require it to be 0? */
260 ret = wtap_file_read_number (wth, &mediaType, err, err_info);
264 *err = WTAP_ERR_BAD_FILE;
265 *err_info = g_strdup("peektagged: <MediaType> value not found");
269 ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
273 *err = WTAP_ERR_BAD_FILE;
274 *err_info = g_strdup("peektagged: <MediaSubType> tag not found");
277 ret = wtap_file_read_number (wth, &mediaSubType, err, err_info);
281 *err = WTAP_ERR_BAD_FILE;
282 *err_info = g_strdup("peektagged: <MediaSubType> value not found");
285 if (mediaSubType >= NUM_PEEKTAGGED_ENCAPS
286 || peektagged_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
287 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
288 *err_info = g_strdup_printf("peektagged: network type %u unknown or unsupported",
293 ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
297 *err = WTAP_ERR_SHORT_READ;
301 /* skip 8 zero bytes */
302 if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
306 * This is an Peek tagged file.
308 file_encap = peektagged_encap[mediaSubType];
310 wth->file_type = WTAP_FILE_PEEKTAGGED;
311 wth->file_encap = file_encap;
312 wth->subtype_read = peektagged_read;
313 wth->subtype_seek_read = peektagged_seek_read;
314 wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
316 peektagged = (peektagged_t *)g_malloc(sizeof(peektagged_t));
317 wth->priv = (void *)peektagged;
318 switch (mediaSubType) {
320 case PEEKTAGGED_NST_ETHERNET:
321 case PEEKTAGGED_NST_802_11:
322 case PEEKTAGGED_NST_802_11_2:
323 peektagged->has_fcs = FALSE;
326 case PEEKTAGGED_NST_802_11_WITH_FCS:
327 peektagged->has_fcs = TRUE;
331 wth->snapshot_length = 0; /* not available in header */
339 peektagged_utime timestamp;
340 struct ieee_802_11_phdr ieee_802_11;
344 * Process the packet header.
346 * XXX - we should supply the additional radio information;
347 * the pseudo-header should probably be supplied in a fashion
348 * similar to the new BSD radio header, so that the 802.11
349 * dissector can determine which, if any, information items
353 peektagged_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err,
360 gboolean saw_length = FALSE;
361 gboolean saw_timestamp_lower = FALSE;
362 gboolean saw_timestamp_upper = FALSE;
364 /* Extract the fields from the packet header */
366 /* Get the tag and value.
367 XXX - this assumes all values are 4 bytes long. */
368 bytes_read = file_read(tag_value, sizeof tag_value, fh);
369 if (bytes_read != (int) sizeof tag_value) {
370 *err = file_error(fh, err_info);
373 *err = WTAP_ERR_SHORT_READ;
374 else if (bytes_read == 0) {
376 * Short read if we've read something already;
377 * just an EOF if we haven't.
380 *err = WTAP_ERR_SHORT_READ;
385 header_len += (int) sizeof(tag_value);
386 tag = pletohs(&tag_value[0]);
389 case TAG_PEEKTAGGED_LENGTH:
391 *err = WTAP_ERR_BAD_FILE;
392 *err_info = g_strdup("peektagged: record has two length fields");
395 hdr_info->length = pletohl(&tag_value[2]);
399 case TAG_PEEKTAGGED_TIMESTAMP_LOWER:
400 if (saw_timestamp_lower) {
401 *err = WTAP_ERR_BAD_FILE;
402 *err_info = g_strdup("peektagged: record has two timestamp-lower fields");
405 hdr_info->timestamp.lower = pletohl(&tag_value[2]);
406 saw_timestamp_lower = TRUE;
409 case TAG_PEEKTAGGED_TIMESTAMP_UPPER:
410 if (saw_timestamp_upper) {
411 *err = WTAP_ERR_BAD_FILE;
412 *err_info = g_strdup("peektagged: record has two timestamp-upper fields");
415 hdr_info->timestamp.upper = pletohl(&tag_value[2]);
416 saw_timestamp_upper = TRUE;
419 case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
420 /* XXX - not used yet */
423 case TAG_PEEKTAGGED_CHANNEL:
424 hdr_info->ieee_802_11.channel = pletohl(&tag_value[2]);
427 case TAG_PEEKTAGGED_RATE:
428 hdr_info->ieee_802_11.data_rate = pletohl(&tag_value[2]);
431 case TAG_PEEKTAGGED_SIGNAL_PERC:
432 hdr_info->ieee_802_11.signal_level = pletohl(&tag_value[2]);
435 case TAG_PEEKTAGGED_SIGNAL_DBM:
436 /* XXX - not used yet */
439 case TAG_PEEKTAGGED_NOISE_PERC:
440 /* XXX - not used yet */
443 case TAG_PEEKTAGGED_NOISE_DBM:
444 /* XXX - not used yet */
447 case TAG_PEEKTAGGED_UNKNOWN_0x000D:
448 /* XXX - seen in an EtherPeek capture; value unknown */
451 case TAG_PEEKTAGGED_SLICE_LENGTH:
452 hdr_info->sliceLength = pletohl(&tag_value[2]);
458 } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH); /* last tag */
461 *err = WTAP_ERR_BAD_FILE;
462 *err_info = g_strdup("peektagged: record has no length field");
465 if (!saw_timestamp_lower) {
466 *err = WTAP_ERR_BAD_FILE;
467 *err_info = g_strdup("peektagged: record has no timestamp-lower field");
470 if (!saw_timestamp_upper) {
471 *err = WTAP_ERR_BAD_FILE;
472 *err_info = g_strdup("peektagged: record has no timestamp-upper field");
480 * Time stamps appear to be in nanoseconds since the Windows epoch
481 * as used in FILETIMEs, i.e. midnight, January 1, 1601.
483 * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
484 * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
485 * that are being subtracted.
487 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
489 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
492 peektagged_t *peektagged = (peektagged_t *)wth->priv;
497 *data_offset = file_tell(wth->fh);
499 /* Process the packet header. */
500 hdrlen = peektagged_process_header(wth->fh, &hdr_info, err, err_info);
505 * If sliceLength is 0, force it to be the actual length of the packet.
507 if (hdr_info.sliceLength == 0)
508 hdr_info.sliceLength = hdr_info.length;
510 if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
512 * Probably a corrupt capture file; don't blow up trying
513 * to allocate space for an immensely-large packet.
515 *err = WTAP_ERR_BAD_FILE;
516 *err_info = g_strdup_printf("peektagged: File has %u-byte packet, bigger than maximum of %u",
517 hdr_info.sliceLength, WTAP_MAX_PACKET_SIZE);
521 wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
523 /* fill in packet header length values before slicelength may be
525 wth->phdr.len = hdr_info.length;
526 wth->phdr.caplen = hdr_info.sliceLength;
528 /* read the frame data */
529 buffer_assure_space(wth->frame_buffer, hdr_info.sliceLength);
530 wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
531 hdr_info.sliceLength, wth->fh, err,
534 /* recalculate and fill in packet time stamp */
535 t = (double) hdr_info.timestamp.lower +
536 (double) hdr_info.timestamp.upper * 4294967296.0;
539 t -= TIME_FIXUP_CONSTANT;
540 wth->phdr.ts.secs = (time_t) t;
541 wth->phdr.ts.nsecs = (guint32) ((t - wth->phdr.ts.secs)*1000000000);
543 switch (wth->file_encap) {
545 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
547 * The last 4 bytes sometimes contains the FCS but on a lot of
548 * interfaces these are zero. Is there some way to determine
549 * from the packet header whether it's an FCS or not?
551 * For now, we just discard those bytes; if we can determine
552 * whether it's an FCS or not, we should use that to determine
553 * whether to supply it as an FCS or discard it.
555 wth->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
556 if (peektagged->has_fcs)
557 wth->pseudo_header.ieee_802_11.fcs_len = 4;
559 wth->pseudo_header.ieee_802_11.fcs_len = 0;
561 wth->phdr.caplen -= 4;
563 wth->pseudo_header.ieee_802_11.decrypted = FALSE;
566 case WTAP_ENCAP_ETHERNET:
568 * The last 4 bytes appear to be 0 in the captures I've seen;
569 * are there any captures where it's an FCS?
571 wth->pseudo_header.eth.fcs_len = 0;
573 wth->phdr.caplen -= 4;
582 peektagged_seek_read(wtap *wth, gint64 seek_off,
583 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
584 int *err, gchar **err_info)
586 peektagged_t *peektagged = (peektagged_t *)wth->priv;
589 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
592 /* Process the packet header. */
593 if (peektagged_process_header(wth->random_fh, &hdr_info, err, err_info) == -1)
596 switch (wth->file_encap) {
598 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
599 pseudo_header->ieee_802_11 = hdr_info.ieee_802_11;
600 if (peektagged->has_fcs)
601 pseudo_header->ieee_802_11.fcs_len = 4;
603 pseudo_header->ieee_802_11.fcs_len = 0;
604 pseudo_header->ieee_802_11.decrypted = FALSE;
607 case WTAP_ENCAP_ETHERNET:
608 pseudo_header->eth.fcs_len = 0;
613 * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
615 errno = WTAP_ERR_CANT_READ;
616 wtap_file_read_expected_bytes(pd, length, wth->random_fh, err, err_info);