5dfff3ebeb36ed0897d17d596a75432fc78e90d7
[metze/wireshark/wip.git] / wiretap / peektagged.c
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).
7  *
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.
14  *
15  * $Id$
16  *
17  * Wiretap Library
18  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
19  *
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.
24  *
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.
29  *
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.
33  */
34
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"
43
44 /* CREDITS
45  *
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).
50  */
51
52 /*
53  * Section header.
54  *
55  * A Peek tagged file consists of multiple sections, each of which begins
56  * with a header in the following format.
57  *
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.
63  *
64  * Files we've seen have the following sections, in order:
65  *
66  * "\177vers" - version information.  The contents are XML, giving
67  * the file format version and application version information.
68  *
69  * "sess" - capture session information.  The contents are XML, giving
70  * various information about the capture session.
71  *
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.
75  */
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;
81
82 /*
83  * Network subtype values.
84  *
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?
88  */
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 */
93
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
107
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;
113
114 typedef struct {
115         gboolean        has_fcs;
116 } peektagged_t;
117
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, guint8 *pd, int length,
122     int *err, gchar **err_info);
123
124 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
125                                 gchar **err_info)
126 {
127     int c;
128     const char *cp;
129
130     cp = pattern;
131     while (*cp)
132     {
133         c = file_getc(wth->fh);
134         if (c == EOF)
135         {
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 */
140         }
141         if (c == *cp)
142             cp++;
143         else
144         {
145             if (c == pattern[0])
146                 cp = &pattern[1];
147             else
148                 cp = pattern;
149         }
150     }
151     return (*cp == '\0' ? 1 : 0);
152 }
153
154
155 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
156                                         const char *separators, int *err,
157                                         gchar **err_info)
158 {
159     int c;
160     char *cp;
161     int i;
162
163     for (cp = buffer, i = 0; i < buflen; i++, cp++)
164     {
165         c = file_getc(wth->fh);
166         if (c == EOF)
167         {
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 */
172         }
173         if (strchr (separators, c) != NULL)
174         {
175             *cp = '\0';
176             break;
177         }
178         else
179             *cp = c;
180     }
181     return i;
182 }
183
184
185 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
186                                 gchar **err_info)
187 {
188     int ret;
189     char str_num[12];
190     unsigned long value;
191     char *p;
192
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;
199     }
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;
205 }
206
207
208 int peektagged_open(wtap *wth, int *err, gchar **err_info)
209 {
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
222     };
223     #define NUM_PEEKTAGGED_ENCAPS (sizeof peektagged_encap / sizeof peektagged_encap[0])
224     peektagged_t *peektagged;
225
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;
232     }
233
234     if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
235         return 0;       /* doesn't begin with a "\177ver" section */
236
237     /*
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).
244      */
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;
250     }
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;
256     }
257
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;
265     }
266
267     /*
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).
276      */
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;
284     }
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;
294     }
295
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;
303     }
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;
311     }
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;
318     }
319
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;
326     }
327
328     /* skip 8 zero bytes */
329     if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
330         return 0;
331
332     /*
333      * This is an Peek tagged file.
334      */
335     file_encap = peektagged_encap[mediaSubType];
336
337     wth->file_type = WTAP_FILE_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;
342
343     peektagged = (peektagged_t *)g_malloc(sizeof(peektagged_t));
344     wth->priv = (void *)peektagged;
345     switch (mediaSubType) {
346
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;
352
353     case PEEKTAGGED_NST_802_11_WITH_FCS:
354         peektagged->has_fcs = TRUE;
355         break;
356     }
357
358     wth->snapshot_length   = 0; /* not available in header */
359
360     return 1;
361 }
362
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;
369
370 /*
371  * Time stamps appear to be in nanoseconds since the Windows epoch
372  * as used in FILETIMEs, i.e. midnight, January 1, 1601.
373  *
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.
377  */
378 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
379
380 /*
381  * Process the packet header.
382  *
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.
388  */
389 static int
390 peektagged_process_header(wtap *wth, FILE_T fh, struct wtap_pkthdr *phdr,
391     int *err, gchar **err_info)
392 {
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;
404
405     hdr_info.length      = 0;
406     hdr_info.sliceLength = 0;
407
408     /* Extract the fields from the packet header */
409     do {
410         /* Get the tag and value.
411            XXX - this assumes all values are 4 bytes long. */
412         bytes_read = file_read(tag_value, sizeof tag_value, fh);
413         if (bytes_read != (int) sizeof tag_value) {
414             *err = file_error(fh, err_info);
415             if (*err == 0) {
416                 if (bytes_read > 0)
417                     *err = WTAP_ERR_SHORT_READ;
418                 else if (bytes_read == 0) {
419                     /*
420                      * Short read if we've read something already;
421                      * just an EOF if we haven't.
422                      */
423                     if (header_len != 0)
424                         *err = WTAP_ERR_SHORT_READ;
425                 }
426             }
427             return -1;
428         }
429         header_len += (int) sizeof(tag_value);
430         tag = pletohs(&tag_value[0]);
431         switch (tag) {
432
433         case TAG_PEEKTAGGED_LENGTH:
434             if (saw_length) {
435                 *err = WTAP_ERR_BAD_FILE;
436                 *err_info = g_strdup("peektagged: record has two length fields");
437                 return -1;
438             }
439             hdr_info.length = pletohl(&tag_value[2]);
440             saw_length = TRUE;
441             break;
442     
443         case TAG_PEEKTAGGED_TIMESTAMP_LOWER:
444             if (saw_timestamp_lower) {
445                 *err = WTAP_ERR_BAD_FILE;
446                 *err_info = g_strdup("peektagged: record has two timestamp-lower fields");
447                 return -1;
448             }
449             hdr_info.timestamp.lower = pletohl(&tag_value[2]);
450             saw_timestamp_lower = TRUE;
451             break;
452
453         case TAG_PEEKTAGGED_TIMESTAMP_UPPER:
454             if (saw_timestamp_upper) {
455                 *err = WTAP_ERR_BAD_FILE;
456                 *err_info = g_strdup("peektagged: record has two timestamp-upper fields");
457                 return -1;
458             }
459             hdr_info.timestamp.upper = pletohl(&tag_value[2]);
460             saw_timestamp_upper = TRUE;
461             break;
462
463         case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
464             /* XXX - not used yet */
465             break;
466
467         case TAG_PEEKTAGGED_CHANNEL:
468             hdr_info.ieee_802_11.channel = pletohl(&tag_value[2]);
469             break;
470
471         case TAG_PEEKTAGGED_RATE:
472             hdr_info.ieee_802_11.data_rate = pletohl(&tag_value[2]);
473             break;
474
475         case TAG_PEEKTAGGED_SIGNAL_PERC:
476             hdr_info.ieee_802_11.signal_level = pletohl(&tag_value[2]);
477             break;
478
479         case TAG_PEEKTAGGED_SIGNAL_DBM:
480             /* XXX - not used yet */
481             break;
482
483         case TAG_PEEKTAGGED_NOISE_PERC:
484             /* XXX - not used yet */
485             break;
486
487         case TAG_PEEKTAGGED_NOISE_DBM:
488             /* XXX - not used yet */
489             break;
490
491         case TAG_PEEKTAGGED_UNKNOWN_0x000D:
492             /* XXX - seen in an EtherPeek capture; value unknown */
493             break;
494
495         case TAG_PEEKTAGGED_SLICE_LENGTH:
496             hdr_info.sliceLength = pletohl(&tag_value[2]);
497             break;
498
499         default:
500             break;
501         }
502     } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH);       /* last tag */
503
504     if (!saw_length) {
505         *err = WTAP_ERR_BAD_FILE;
506         *err_info = g_strdup("peektagged: record has no length field");
507         return -1;
508     }
509     if (!saw_timestamp_lower) {
510         *err = WTAP_ERR_BAD_FILE;
511         *err_info = g_strdup("peektagged: record has no timestamp-lower field");
512         return -1;
513     }
514     if (!saw_timestamp_upper) {
515         *err = WTAP_ERR_BAD_FILE;
516         *err_info = g_strdup("peektagged: record has no timestamp-upper field");
517         return -1;
518     }
519
520     /*
521      * If sliceLength is 0, force it to be the actual length of the packet.
522      */
523     if (hdr_info.sliceLength == 0)
524         hdr_info.sliceLength = hdr_info.length;
525
526     if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
527         /*
528          * Probably a corrupt capture file; don't blow up trying
529          * to allocate space for an immensely-large packet.
530          */
531         *err = WTAP_ERR_BAD_FILE;
532         *err_info = g_strdup_printf("peektagged: File has %u-byte packet, bigger than maximum of %u",
533             hdr_info.sliceLength, WTAP_MAX_PACKET_SIZE);
534         return -1;
535     }
536
537     phdr->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
538     phdr->len    = hdr_info.length;
539     phdr->caplen = hdr_info.sliceLength;
540
541     /* calculate and fill in packet time stamp */
542     t =  (double) hdr_info.timestamp.lower +
543          (double) hdr_info.timestamp.upper * 4294967296.0;
544     t *= 1.0e-9;
545     t -= TIME_FIXUP_CONSTANT;
546     phdr->ts.secs  = (time_t) t;
547     phdr->ts.nsecs = (guint32) ((t - phdr->ts.secs)*1000000000);
548
549     switch (wth->file_encap) {
550
551     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
552         phdr->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
553         if (peektagged->has_fcs)
554             phdr->pseudo_header.ieee_802_11.fcs_len = 4;
555         else {
556             if (phdr->len < 4 || phdr->caplen < 4) {
557                 *err = WTAP_ERR_BAD_FILE;
558                 *err_info = g_strdup_printf("peektagged: 802.11 packet has length < 4");
559                 return FALSE;
560             }
561             phdr->pseudo_header.ieee_802_11.fcs_len = 0;
562             phdr->len -= 4;
563             phdr->caplen -= 4;
564             skip_len = 4;
565         }
566         phdr->pseudo_header.ieee_802_11.decrypted = FALSE;
567         break;
568
569     case WTAP_ENCAP_ETHERNET:
570         /*
571          * The last 4 bytes appear to be 0 in the captures I've seen;
572          * are there any captures where it's an FCS?
573          */
574         if (phdr->len < 4 || phdr->caplen < 4) {
575             *err = WTAP_ERR_BAD_FILE;
576             *err_info = g_strdup_printf("peektagged: Ethernet packet has length < 4");
577             return FALSE;
578         }
579         phdr->pseudo_header.eth.fcs_len = 0;
580         phdr->len -= 4;
581         phdr->caplen -= 4;
582         skip_len = 4;
583         break;
584     }
585
586     return skip_len;
587 }
588
589 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
590     gint64 *data_offset)
591 {
592     int skip_len;
593
594     *data_offset = file_tell(wth->fh);
595
596     /* Process the packet header. */
597     skip_len = peektagged_process_header(wth, wth->fh, &wth->phdr, err, err_info);
598     if (skip_len == -1)
599         return FALSE;
600
601     /* read the frame data */
602     buffer_assure_space(wth->frame_buffer, wth->phdr.caplen);
603     wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
604                                   wth->phdr.caplen, wth->fh, err,
605                                   err_info);
606
607     if (skip_len != 0) {
608         /* Skip extra junk at the end of the packet data. */
609         if (!file_skip(wth->fh, skip_len, err))
610             return FALSE;
611     }
612
613     return TRUE;
614 }
615
616 static gboolean
617 peektagged_seek_read(wtap *wth, gint64 seek_off,
618     struct wtap_pkthdr *phdr, guint8 *pd, int length,
619     int *err, gchar **err_info)
620 {
621     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
622         return FALSE;
623
624     /* Process the packet header. */
625     if (peektagged_process_header(wth, wth->random_fh, phdr, err, err_info) == -1)
626         return FALSE;
627
628     /*
629      * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
630      */
631     errno = WTAP_ERR_CANT_READ;
632     wtap_file_read_expected_bytes(pd, length, wth->random_fh, err, err_info);
633     return TRUE;
634 }