DCERPC: factor out proto_tree_add_dcerpc_drep()
[wireshark-wip.git] / wiretap / peektagged.c
blobd61925191fbac4554519b1372ea986922702a4ac
1 /* peektagged.c
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
13 * products.
15 * $Id$
17 * Wiretap Library
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.
35 #include "config.h"
36 #include <errno.h>
37 #include <string.h>
38 #include <stdlib.h>
39 #include "wtap-int.h"
40 #include "file_wrappers.h"
41 #include "buffer.h"
42 #include "peektagged.h"
44 /* CREDITS
46 * This file decoder could not have been writen without examining
47 * http://www.varsanofiev.com/inside/airopeekv9.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 * Section header.
55 * A Peek tagged file consists of multiple sections, each of which begins
56 * with a header in the following format.
58 * The section ID is a 4-character string saying what type of section
59 * it is. The section length is a little-endian field giving the
60 * length of the section, in bytes, including the section header
61 * itself. The other field of the section header is a little-endian
62 * constant that always appears to be 0x00000200.
64 * Files we've seen have the following sections, in order:
66 * "\177vers" - version information. The contents are XML, giving
67 * the file format version and application version information.
69 * "sess" - capture session information. The contents are XML, giving
70 * various information about the capture session.
72 * "pkts" - captured packets. The contents are binary records, one for
73 * each packet, with the record being a list of tagged values followed
74 * by the raw packet data.
76 typedef struct peektagged_section_header {
77 gint8 section_id[4]; /* string identifying the section */
78 guint32 section_len; /* little-endian section length */
79 guint32 section_const; /* little-endian 0x00000200 */
80 } peektagged_section_header_t;
83 * Network subtype values.
85 * XXX - do different network subtype values for 802.11 indicate different
86 * network adapter types, with some adapters supplying the FCS and others
87 * not supplying the FCS?
89 #define PEEKTAGGED_NST_ETHERNET 0
90 #define PEEKTAGGED_NST_802_11 1 /* 802.11 with 0's at the end */
91 #define PEEKTAGGED_NST_802_11_2 2 /* 802.11 with 0's at the end */
92 #define PEEKTAGGED_NST_802_11_WITH_FCS 3 /* 802.11 with FCS at the end */
94 /* tags for fields in packet header */
95 #define TAG_PEEKTAGGED_LENGTH 0x0000
96 #define TAG_PEEKTAGGED_TIMESTAMP_LOWER 0x0001
97 #define TAG_PEEKTAGGED_TIMESTAMP_UPPER 0x0002
98 #define TAG_PEEKTAGGED_FLAGS_AND_STATUS 0x0003
99 #define TAG_PEEKTAGGED_CHANNEL 0x0004
100 #define TAG_PEEKTAGGED_RATE 0x0005
101 #define TAG_PEEKTAGGED_SIGNAL_PERC 0x0006
102 #define TAG_PEEKTAGGED_SIGNAL_DBM 0x0007
103 #define TAG_PEEKTAGGED_NOISE_PERC 0x0008
104 #define TAG_PEEKTAGGED_NOISE_DBM 0x0009
105 #define TAG_PEEKTAGGED_UNKNOWN_0x000D 0x000D
106 #define TAG_PEEKTAGGED_SLICE_LENGTH 0xffff
108 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
109 typedef struct peektagged_utime {
110 guint32 upper;
111 guint32 lower;
112 } peektagged_utime;
114 typedef struct {
115 gboolean has_fcs;
116 } peektagged_t;
118 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
119 gint64 *data_offset);
120 static gboolean peektagged_seek_read(wtap *wth, gint64 seek_off,
121 struct wtap_pkthdr *phdr, Buffer *buf, int length,
122 int *err, gchar **err_info);
124 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
125 gchar **err_info)
127 int c;
128 const char *cp;
130 cp = pattern;
131 while (*cp)
133 c = file_getc(wth->fh);
134 if (c == EOF)
136 *err = file_error(wth->fh, err_info);
137 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
138 return -1; /* error */
139 return 0; /* EOF */
141 if (c == *cp)
142 cp++;
143 else
145 if (c == pattern[0])
146 cp = &pattern[1];
147 else
148 cp = pattern;
151 return (*cp == '\0' ? 1 : 0);
155 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
156 const char *separators, int *err,
157 gchar **err_info)
159 int c;
160 char *cp;
161 int i;
163 for (cp = buffer, i = 0; i < buflen; i++, cp++)
165 c = file_getc(wth->fh);
166 if (c == EOF)
168 *err = file_error(wth->fh, err_info);
169 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
170 return -1; /* error */
171 return 0; /* EOF */
173 if (strchr (separators, c) != NULL)
175 *cp = '\0';
176 break;
178 else
179 *cp = c;
181 return i;
185 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
186 gchar **err_info)
188 int ret;
189 char str_num[12];
190 unsigned long value;
191 char *p;
193 ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
194 err, err_info);
195 if (ret != 1) {
196 /* 0 means EOF, which means "not a valid Peek tagged file";
197 -1 means error, and "err" has been set. */
198 return ret;
200 value = strtoul (str_num, &p, 10);
201 if (p == str_num || value > G_MAXUINT32)
202 return 0;
203 *num = (guint32)value;
204 return 1;
208 int peektagged_open(wtap *wth, int *err, gchar **err_info)
210 peektagged_section_header_t ap_hdr;
211 int bytes_read;
212 int ret;
213 guint32 fileVersion;
214 guint32 mediaType;
215 guint32 mediaSubType = 0;
216 int file_encap;
217 static const int peektagged_encap[] = {
218 WTAP_ENCAP_ETHERNET,
219 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
220 WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
221 WTAP_ENCAP_IEEE_802_11_WITH_RADIO
223 #define NUM_PEEKTAGGED_ENCAPS (sizeof peektagged_encap / sizeof peektagged_encap[0])
224 peektagged_t *peektagged;
226 bytes_read = file_read(&ap_hdr, (int)sizeof(ap_hdr), wth->fh);
227 if (bytes_read != (int)sizeof(ap_hdr)) {
228 *err = file_error(wth->fh, err_info);
229 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
230 return -1;
231 return 0;
234 if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
235 return 0; /* doesn't begin with a "\177ver" section */
238 * XXX - we should get the length of the "\177ver" section, check
239 * that it's followed by a little-endian 0x00000200, and then,
240 * when reading the XML, make sure we don't go past the end of
241 * that section, and skip to the end of that section when
242 * we have the file version (and possibly check to make sure all
243 * tags are properly opened and closed).
245 ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
246 if (ret != 1) {
247 /* 0 means EOF, which means "not a valid Peek tagged file";
248 -1 means error, and "err" has been set. */
249 return ret;
251 ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
252 if (ret != 1) {
253 /* 0 means EOF, which means "not a valid Peek tagged file";
254 -1 means error, and "err" has been set. */
255 return ret;
258 /* If we got this far, we assume it's a Peek tagged file. */
259 if (fileVersion != 9) {
260 /* We only support version 9. */
261 *err = WTAP_ERR_UNSUPPORTED;
262 *err_info = g_strdup_printf("peektagged: version %u unsupported",
263 fileVersion);
264 return -1;
268 * XXX - once we've skipped the "\177ver" section, we should
269 * check for a "sess" section and fail if we don't see it.
270 * Then we should get the length of the "sess" section, check
271 * that it's followed by a little-endian 0x00000200, and then,
272 * when reading the XML, make sure we don't go past the end of
273 * that section, and skip to the end of the section when
274 * we have the file version (and possibly check to make sure all
275 * tags are properly opened and closed).
277 ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
278 if (ret == -1)
279 return -1;
280 if (ret == 0) {
281 *err = WTAP_ERR_BAD_FILE;
282 *err_info = g_strdup("peektagged: <MediaType> tag not found");
283 return -1;
285 /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
286 files we've seen; should we require it to be 0? */
287 ret = wtap_file_read_number (wth, &mediaType, err, err_info);
288 if (ret == -1)
289 return -1;
290 if (ret == 0) {
291 *err = WTAP_ERR_BAD_FILE;
292 *err_info = g_strdup("peektagged: <MediaType> value not found");
293 return -1;
296 ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
297 if (ret == -1)
298 return -1;
299 if (ret == 0) {
300 *err = WTAP_ERR_BAD_FILE;
301 *err_info = g_strdup("peektagged: <MediaSubType> tag not found");
302 return -1;
304 ret = wtap_file_read_number (wth, &mediaSubType, err, err_info);
305 if (ret == -1)
306 return -1;
307 if (ret == 0) {
308 *err = WTAP_ERR_BAD_FILE;
309 *err_info = g_strdup("peektagged: <MediaSubType> value not found");
310 return -1;
312 if (mediaSubType >= NUM_PEEKTAGGED_ENCAPS
313 || peektagged_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
314 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
315 *err_info = g_strdup_printf("peektagged: network type %u unknown or unsupported",
316 mediaSubType);
317 return -1;
320 ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
321 if (ret == -1)
322 return -1;
323 if (ret == 0) {
324 *err = WTAP_ERR_SHORT_READ;
325 return -1;
328 /* skip 8 zero bytes */
329 if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
330 return 0;
333 * This is an Peek tagged file.
335 file_encap = peektagged_encap[mediaSubType];
337 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_PEEKTAGGED;
338 wth->file_encap = file_encap;
339 wth->subtype_read = peektagged_read;
340 wth->subtype_seek_read = peektagged_seek_read;
341 wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
343 peektagged = (peektagged_t *)g_malloc(sizeof(peektagged_t));
344 wth->priv = (void *)peektagged;
345 switch (mediaSubType) {
347 case PEEKTAGGED_NST_ETHERNET:
348 case PEEKTAGGED_NST_802_11:
349 case PEEKTAGGED_NST_802_11_2:
350 peektagged->has_fcs = FALSE;
351 break;
353 case PEEKTAGGED_NST_802_11_WITH_FCS:
354 peektagged->has_fcs = TRUE;
355 break;
358 wth->snapshot_length = 0; /* not available in header */
360 return 1;
363 typedef struct {
364 guint32 length;
365 guint32 sliceLength;
366 peektagged_utime timestamp;
367 struct ieee_802_11_phdr ieee_802_11;
368 } hdr_info_t;
371 * Time stamps appear to be in nanoseconds since the Windows epoch
372 * as used in FILETIMEs, i.e. midnight, January 1, 1601.
374 * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
375 * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
376 * that are being subtracted.
378 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
381 * Read the packet.
383 * XXX - we should supply the additional radio information;
384 * the pseudo-header should probably be supplied in a fashion
385 * similar to the radiotap radio header, so that the 802.11
386 * dissector can determine which, if any, information items
387 * are present.
389 static int
390 peektagged_read_packet(wtap *wth, FILE_T fh, struct wtap_pkthdr *phdr,
391 Buffer *buf, int *err, gchar **err_info)
393 peektagged_t *peektagged = (peektagged_t *)wth->priv;
394 hdr_info_t hdr_info;
395 int header_len = 0;
396 int bytes_read;
397 guint8 tag_value[6];
398 guint16 tag;
399 gboolean saw_length = FALSE;
400 gboolean saw_timestamp_lower = FALSE;
401 gboolean saw_timestamp_upper = FALSE;
402 int skip_len = 0;
403 double t;
405 memset(&hdr_info, 0, sizeof(hdr_info_t));
407 /* Extract the fields from the packet header */
408 do {
409 /* Get the tag and value.
410 XXX - this assumes all values are 4 bytes long. */
411 bytes_read = file_read(tag_value, sizeof tag_value, fh);
412 if (bytes_read != (int) sizeof tag_value) {
413 *err = file_error(fh, err_info);
414 if (*err == 0) {
415 if (bytes_read > 0)
416 *err = WTAP_ERR_SHORT_READ;
417 else if (bytes_read == 0) {
419 * Short read if we've read something already;
420 * just an EOF if we haven't.
422 if (header_len != 0)
423 *err = WTAP_ERR_SHORT_READ;
426 return -1;
428 header_len += (int) sizeof(tag_value);
429 tag = pletohs(&tag_value[0]);
430 switch (tag) {
432 case TAG_PEEKTAGGED_LENGTH:
433 if (saw_length) {
434 *err = WTAP_ERR_BAD_FILE;
435 *err_info = g_strdup("peektagged: record has two length fields");
436 return -1;
438 hdr_info.length = pletohl(&tag_value[2]);
439 saw_length = TRUE;
440 break;
442 case TAG_PEEKTAGGED_TIMESTAMP_LOWER:
443 if (saw_timestamp_lower) {
444 *err = WTAP_ERR_BAD_FILE;
445 *err_info = g_strdup("peektagged: record has two timestamp-lower fields");
446 return -1;
448 hdr_info.timestamp.lower = pletohl(&tag_value[2]);
449 saw_timestamp_lower = TRUE;
450 break;
452 case TAG_PEEKTAGGED_TIMESTAMP_UPPER:
453 if (saw_timestamp_upper) {
454 *err = WTAP_ERR_BAD_FILE;
455 *err_info = g_strdup("peektagged: record has two timestamp-upper fields");
456 return -1;
458 hdr_info.timestamp.upper = pletohl(&tag_value[2]);
459 saw_timestamp_upper = TRUE;
460 break;
462 case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
463 /* XXX - not used yet */
464 break;
466 case TAG_PEEKTAGGED_CHANNEL:
467 hdr_info.ieee_802_11.channel = pletohl(&tag_value[2]);
468 break;
470 case TAG_PEEKTAGGED_RATE:
471 hdr_info.ieee_802_11.data_rate = pletohl(&tag_value[2]);
472 break;
474 case TAG_PEEKTAGGED_SIGNAL_PERC:
475 hdr_info.ieee_802_11.signal_level = pletohl(&tag_value[2]);
476 break;
478 case TAG_PEEKTAGGED_SIGNAL_DBM:
479 /* XXX - not used yet */
480 break;
482 case TAG_PEEKTAGGED_NOISE_PERC:
483 /* XXX - not used yet */
484 break;
486 case TAG_PEEKTAGGED_NOISE_DBM:
487 /* XXX - not used yet */
488 break;
490 case TAG_PEEKTAGGED_UNKNOWN_0x000D:
491 /* XXX - seen in an EtherPeek capture; value unknown */
492 break;
494 case TAG_PEEKTAGGED_SLICE_LENGTH:
495 hdr_info.sliceLength = pletohl(&tag_value[2]);
496 break;
498 default:
499 break;
501 } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH); /* last tag */
503 if (!saw_length) {
504 *err = WTAP_ERR_BAD_FILE;
505 *err_info = g_strdup("peektagged: record has no length field");
506 return -1;
508 if (!saw_timestamp_lower) {
509 *err = WTAP_ERR_BAD_FILE;
510 *err_info = g_strdup("peektagged: record has no timestamp-lower field");
511 return -1;
513 if (!saw_timestamp_upper) {
514 *err = WTAP_ERR_BAD_FILE;
515 *err_info = g_strdup("peektagged: record has no timestamp-upper field");
516 return -1;
520 * If sliceLength is 0, force it to be the actual length of the packet.
522 if (hdr_info.sliceLength == 0)
523 hdr_info.sliceLength = hdr_info.length;
525 if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
527 * Probably a corrupt capture file; don't blow up trying
528 * to allocate space for an immensely-large packet.
530 *err = WTAP_ERR_BAD_FILE;
531 *err_info = g_strdup_printf("peektagged: File has %u-byte packet, bigger than maximum of %u",
532 hdr_info.sliceLength, WTAP_MAX_PACKET_SIZE);
533 return -1;
536 phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
537 phdr->len = hdr_info.length;
538 phdr->caplen = hdr_info.sliceLength;
540 /* calculate and fill in packet time stamp */
541 t = (double) hdr_info.timestamp.lower +
542 (double) hdr_info.timestamp.upper * 4294967296.0;
543 t *= 1.0e-9;
544 t -= TIME_FIXUP_CONSTANT;
545 phdr->ts.secs = (time_t) t;
546 phdr->ts.nsecs = (guint32) ((t - phdr->ts.secs)*1000000000);
548 switch (wth->file_encap) {
550 case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
551 phdr->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
552 if (peektagged->has_fcs)
553 phdr->pseudo_header.ieee_802_11.fcs_len = 4;
554 else {
555 if (phdr->len < 4 || phdr->caplen < 4) {
556 *err = WTAP_ERR_BAD_FILE;
557 *err_info = g_strdup_printf("peektagged: 802.11 packet has length < 4");
558 return FALSE;
560 phdr->pseudo_header.ieee_802_11.fcs_len = 0;
561 phdr->len -= 4;
562 phdr->caplen -= 4;
563 skip_len = 4;
565 phdr->pseudo_header.ieee_802_11.decrypted = FALSE;
566 break;
568 case WTAP_ENCAP_ETHERNET:
570 * The last 4 bytes appear to be 0 in the captures I've seen;
571 * are there any captures where it's an FCS?
573 if (phdr->len < 4 || phdr->caplen < 4) {
574 *err = WTAP_ERR_BAD_FILE;
575 *err_info = g_strdup_printf("peektagged: Ethernet packet has length < 4");
576 return FALSE;
578 phdr->pseudo_header.eth.fcs_len = 0;
579 phdr->len -= 4;
580 phdr->caplen -= 4;
581 skip_len = 4;
582 break;
585 /* Read the packet data. */
586 if (!wtap_read_packet_bytes(fh, buf, phdr->caplen, err, err_info))
587 return -1;
589 return skip_len;
592 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
593 gint64 *data_offset)
595 int skip_len;
597 *data_offset = file_tell(wth->fh);
599 /* Read the packet. */
600 skip_len = peektagged_read_packet(wth, wth->fh, &wth->phdr,
601 wth->frame_buffer, err, err_info);
602 if (skip_len == -1)
603 return FALSE;
605 if (skip_len != 0) {
606 /* Skip extra junk at the end of the packet data. */
607 if (!file_skip(wth->fh, skip_len, err))
608 return FALSE;
611 return TRUE;
614 static gboolean
615 peektagged_seek_read(wtap *wth, gint64 seek_off,
616 struct wtap_pkthdr *phdr, Buffer *buf, int length _U_,
617 int *err, gchar **err_info)
619 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
620 return FALSE;
622 /* Read the packet. */
623 if (peektagged_read_packet(wth, wth->random_fh, phdr, buf, err, err_info) == -1) {
624 if (*err == 0)
625 *err = WTAP_ERR_SHORT_READ;
626 return FALSE;
628 return TRUE;