Manually revert the changes to CMakeLists.txt from commit 40602 - they should not...
[obnox/wireshark/wip.git] / wiretap / airopeek9.c
1 /* airopeek9.c
2  * Routines for opening EtherPeek and AiroPeek V9 files
3  *
4  * $Id$
5  *
6  * Wiretap Library
7  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #include <errno.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include "wtap-int.h"
31 #include "file_wrappers.h"
32 #include "buffer.h"
33 #include "airopeek9.h"
34
35 /* CREDITS
36  *
37  * This file decoder could not have been writen without examining
38  * http://www.varsanofiev.com/inside/airopeekv9.htm, the help from
39  * Martin Regner and Guy Harris, and the etherpeek.c file.
40  */
41
42 /*
43  * NOTE: it says "airopeek" because the first files seen that use this
44  * format were AiroPeek files; however, EtherPeek files using it have
45  * also been seen.
46  */
47
48 /* section header */
49 typedef struct airopeek_section_header {
50         gint8   section_id[4];
51         guint32 section_len;
52         guint32 section_const;
53 } airopeek_section_header_t;
54
55 /*
56  * Network subtype values.
57  *
58  * XXX - do different network subtype values for 802.11 indicate different
59  * network adapter types, with some adapters supplying the FCS and others
60  * not supplying the FCS?
61  */
62 #define AIROPEEK_V9_NST_ETHERNET                0
63 #define AIROPEEK_V9_NST_802_11                  1       /* 802.11 with 0's at the end */
64 #define AIROPEEK_V9_NST_802_11_2                2       /* 802.11 with 0's at the end */
65 #define AIROPEEK_V9_NST_802_11_WITH_FCS         3       /* 802.11 with FCS at the end */
66
67 /* tags for fields in packet header */
68 #define TAG_AIROPEEK_V9_LENGTH                  0x0000
69 #define TAG_AIROPEEK_V9_TIMESTAMP_LOWER         0x0001
70 #define TAG_AIROPEEK_V9_TIMESTAMP_UPPER         0x0002
71 #define TAG_AIROPEEK_V9_FLAGS_AND_STATUS        0x0003
72 #define TAG_AIROPEEK_V9_CHANNEL                 0x0004
73 #define TAG_AIROPEEK_V9_RATE                    0x0005
74 #define TAG_AIROPEEK_V9_SIGNAL_PERC             0x0006
75 #define TAG_AIROPEEK_V9_SIGNAL_DBM              0x0007
76 #define TAG_AIROPEEK_V9_NOISE_PERC              0x0008
77 #define TAG_AIROPEEK_V9_NOISE_DBM               0x0009
78 #define TAG_AIROPEEK_V9_UNKNOWN_0x000D          0x000D
79 #define TAG_AIROPEEK_V9_SLICE_LENGTH            0xffff
80
81 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
82 typedef struct airopeek_utime {
83         guint32 upper;
84         guint32 lower;
85 } airopeek_utime;
86
87 typedef struct {
88         gboolean        has_fcs;
89 } airopeek9_t;
90
91 static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
92     gint64 *data_offset);
93 static gboolean airopeekv9_seek_read(wtap *wth, gint64 seek_off,
94     union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
95     int *err, gchar **err_info);
96
97 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
98                                 gchar **err_info)
99 {
100     int c;
101     const char *cp;
102
103     cp = pattern;
104     while (*cp)
105     {
106         c = file_getc(wth->fh);
107         if (c == EOF) {
108             if (file_eof(wth->fh))
109                 return 0;       /* EOF */
110             else {
111                 *err = file_error(wth->fh, err_info);
112                 return -1;      /* error */
113             }
114         }
115         if (c == *cp)
116             cp++;
117         else
118         {
119             if (c == pattern[0])
120                 cp = &pattern[1];
121             else
122                 cp = pattern;
123         }
124     }
125     return (*cp == '\0' ? 1 : 0);
126 }
127
128
129 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
130                                         const char *separators, int *err,
131                                         gchar **err_info)
132 {
133     int c;
134     char *cp;
135     int i;
136
137     for (cp = buffer, i = 0; i < buflen; i++, cp++)
138     {
139         c = file_getc(wth->fh);
140         if (c == EOF) {
141             if (file_eof(wth->fh))
142                 return 0;       /* EOF */
143             else {
144                 *err = file_error(wth->fh, err_info);
145                 return -1;      /* error */
146             }
147         }
148         if (strchr (separators, c) != NULL)
149         {
150             *cp = '\0';
151             break;
152         }
153         else
154             *cp = c;
155     }
156     return i;
157 }
158
159
160 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
161                                 gchar **err_info)
162 {
163     int ret;
164     char str_num[12];
165     unsigned long value;
166     char *p;
167
168     ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
169                                          err, err_info);
170     if (ret != 1) {
171         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
172            -1 means error, and "err" has been set. */
173         return ret;
174     }
175     value = strtoul (str_num, &p, 10);
176     if (p == str_num || value > G_MAXUINT32)
177         return 0;
178     *num = (guint32)value;
179     return 1;
180 }
181
182
183 int airopeek9_open(wtap *wth, int *err, gchar **err_info)
184 {
185     airopeek_section_header_t ap_hdr;
186     int ret;
187     guint32 fileVersion;
188     guint32 mediaType;
189     guint32 mediaSubType = 0;
190     int file_encap;
191     static const int airopeek9_encap[] = {
192         WTAP_ENCAP_ETHERNET,
193         WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
194         WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
195         WTAP_ENCAP_IEEE_802_11_WITH_RADIO
196     };
197     #define NUM_AIROPEEK9_ENCAPS (sizeof airopeek9_encap / sizeof airopeek9_encap[0])
198     airopeek9_t *airopeek9;
199
200     wtap_file_read_unknown_bytes(&ap_hdr, sizeof(ap_hdr), wth->fh, err,
201                                  err_info);
202
203     if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
204         return 0;       /* doesn't begin with a "\177ver" section */
205
206     /*
207      * XXX - we should get the length of the "\177ver" section, check
208      * that it's followed by a little-endian 0x00000200, and then,
209      * when reading the XML, make sure we don't go past the end of
210      * that section, and skip to the end of that section when
211      * we have the file version (and possibly check to make sure all
212      * tags are properly opened and closed).
213      */
214     ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
215     if (ret != 1) {
216         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
217            -1 means error, and "err" has been set. */
218         return ret;
219     }
220     ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
221     if (ret != 1) {
222         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
223            -1 means error, and "err" has been set. */
224         return ret;
225     }
226
227     /* If we got this far, we assume it's an AiroPeek V9 file. */
228     if (fileVersion != 9) {
229         /* We only support version 9. */
230         *err = WTAP_ERR_UNSUPPORTED;
231         *err_info = g_strdup_printf("airopeekv9: version %u unsupported",
232             fileVersion);
233         return -1;
234     }
235
236     /*
237      * XXX - once we've skipped the "\177ver" section, we should
238      * check for a "sess" section and fail if we don't see it.
239      * Then we should get the length of the "sess" section, check
240      * that it's followed by a little-endian 0x00000200, and then,
241      * when reading the XML, make sure we don't go past the end of
242      * that section, and skip to the end of the section when
243      * we have the file version (and possibly check to make sure all
244      * tags are properly opened and closed).
245      */
246     ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
247     if (ret == -1)
248         return -1;
249     if (ret == 0) {
250         *err = WTAP_ERR_BAD_FILE;
251         *err_info = g_strdup("airopeekv9: <MediaType> tag not found");
252         return -1;
253     }
254     /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
255        files we've seen; should we require it to be 0? */
256     ret = wtap_file_read_number (wth, &mediaType, err, err_info);
257     if (ret == -1)
258         return -1;
259     if (ret == 0) {
260         *err = WTAP_ERR_BAD_FILE;
261         *err_info = g_strdup("airopeekv9: <MediaType> value not found");
262         return -1;
263     }
264
265     ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
266     if (ret == -1)
267         return -1;
268     if (ret == 0) {
269         *err = WTAP_ERR_BAD_FILE;
270         *err_info = g_strdup("airopeekv9: <MediaSubType> tag not found");
271         return -1;
272     }
273     ret = wtap_file_read_number (wth, &mediaSubType, err, err_info);
274     if (ret == -1)
275         return -1;
276     if (ret == 0) {
277         *err = WTAP_ERR_BAD_FILE;
278         *err_info = g_strdup("airopeekv9: <MediaSubType> value not found");
279         return -1;
280     }
281     if (mediaSubType >= NUM_AIROPEEK9_ENCAPS
282         || airopeek9_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
283         *err = WTAP_ERR_UNSUPPORTED_ENCAP;
284         *err_info = g_strdup_printf("airopeekv9: network type %u unknown or unsupported",
285             mediaSubType);
286         return -1;
287     }
288
289     ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
290     if (ret == -1)
291         return -1;
292     if (ret == 0) {
293         *err = WTAP_ERR_SHORT_READ;
294         return -1;
295     }
296
297     /* skip 8 zero bytes */
298     if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
299         return 0;
300
301     /*
302      * This is an EtherPeek or AiroPeek V9 file.
303      */
304     wth->data_offset = file_tell (wth->fh);
305
306     file_encap = airopeek9_encap[mediaSubType];
307
308     wth->file_type = WTAP_FILE_AIROPEEK_V9;
309     wth->file_encap = file_encap;
310     wth->subtype_read = airopeekv9_read;
311     wth->subtype_seek_read = airopeekv9_seek_read;
312     wth->tsprecision = WTAP_FILE_TSPREC_NSEC;
313
314     airopeek9 = (airopeek9_t *)g_malloc(sizeof(airopeek9_t));
315     wth->priv = (void *)airopeek9;
316     switch (mediaSubType) {
317
318     case AIROPEEK_V9_NST_ETHERNET:
319     case AIROPEEK_V9_NST_802_11:
320     case AIROPEEK_V9_NST_802_11_2:
321         airopeek9->has_fcs = FALSE;
322         break;
323
324     case AIROPEEK_V9_NST_802_11_WITH_FCS:
325         airopeek9->has_fcs = TRUE;
326         break;
327     }
328
329     wth->snapshot_length   = 0; /* not available in header */
330
331     return 1;
332 }
333
334 typedef struct {
335     guint32 length;
336     guint32 sliceLength;
337     airopeek_utime timestamp;
338     struct ieee_802_11_phdr ieee_802_11;
339 } hdr_info_t;
340
341 /*
342  * Process the packet header.
343  *
344  * XXX - we should supply the additional radio information;
345  * the pseudo-header should probably be supplied in a fashion
346  * similar to the new BSD radio header, so that the 802.11
347  * dissector can determine which, if any, information items
348  * are present.
349  */
350 static int
351 airopeekv9_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err,
352     gchar **err_info)
353 {
354     int header_len = 0;
355     int bytes_read;
356     guint8 tag_value[6];
357     guint16 tag;
358     gboolean saw_length = FALSE;
359     gboolean saw_timestamp_lower = FALSE;
360     gboolean saw_timestamp_upper = FALSE;
361
362     /* Extract the fields from the packet header */
363     do {
364         /* Get the tag and value.
365            XXX - this assumes all values are 4 bytes long. */
366         bytes_read = file_read(tag_value, sizeof tag_value, fh);
367         if (bytes_read != (int) sizeof tag_value) {
368             *err = file_error(fh, err_info);
369             if (*err == 0) {
370                 if (bytes_read > 0)
371                     *err = WTAP_ERR_SHORT_READ;
372                 else if (bytes_read == 0) {
373                     /*
374                      * Short read if we've read something already;
375                      * just an EOF if we haven't.
376                      */
377                     if (header_len != 0)
378                         *err = WTAP_ERR_SHORT_READ;
379                 }
380             }
381             return 0;
382         }
383         header_len += (int) sizeof(tag_value);
384         tag = pletohs(&tag_value[0]);
385         switch (tag) {
386
387         case TAG_AIROPEEK_V9_LENGTH:
388             if (saw_length) {
389                 *err = WTAP_ERR_BAD_FILE;
390                 *err_info = g_strdup("airopeekv9: record has two length fields");
391                 return 0;
392             }
393             hdr_info->length = pletohl(&tag_value[2]);
394             saw_length = TRUE;
395             break;
396     
397         case TAG_AIROPEEK_V9_TIMESTAMP_LOWER:
398             if (saw_timestamp_lower) {
399                 *err = WTAP_ERR_BAD_FILE;
400                 *err_info = g_strdup("airopeekv9: record has two timestamp-lower fields");
401                 return 0;
402             }
403             hdr_info->timestamp.lower = pletohl(&tag_value[2]);
404             saw_timestamp_lower = TRUE;
405             break;
406
407         case TAG_AIROPEEK_V9_TIMESTAMP_UPPER:
408             if (saw_timestamp_upper) {
409                 *err = WTAP_ERR_BAD_FILE;
410                 *err_info = g_strdup("airopeekv9: record has two timestamp-upper fields");
411                 return 0;
412             }
413             hdr_info->timestamp.upper = pletohl(&tag_value[2]);
414             saw_timestamp_upper = TRUE;
415             break;
416
417         case TAG_AIROPEEK_V9_FLAGS_AND_STATUS:
418             /* XXX - not used yet */
419             break;
420
421         case TAG_AIROPEEK_V9_CHANNEL:
422             hdr_info->ieee_802_11.channel = pletohl(&tag_value[2]);
423             break;
424
425         case TAG_AIROPEEK_V9_RATE:
426             hdr_info->ieee_802_11.data_rate = pletohl(&tag_value[2]);
427             break;
428
429         case TAG_AIROPEEK_V9_SIGNAL_PERC:
430             hdr_info->ieee_802_11.signal_level = pletohl(&tag_value[2]);
431             break;
432
433         case TAG_AIROPEEK_V9_SIGNAL_DBM:
434             /* XXX - not used yet */
435             break;
436
437         case TAG_AIROPEEK_V9_NOISE_PERC:
438             /* XXX - not used yet */
439             break;
440
441         case TAG_AIROPEEK_V9_NOISE_DBM:
442             /* XXX - not used yet */
443             break;
444
445         case TAG_AIROPEEK_V9_UNKNOWN_0x000D:
446             /* XXX - seen in an EtherPeek capture; value unknown */
447             break;
448
449         case TAG_AIROPEEK_V9_SLICE_LENGTH:
450             hdr_info->sliceLength = pletohl(&tag_value[2]);
451             break;
452
453         default:
454             break;
455         }
456     } while (tag != TAG_AIROPEEK_V9_SLICE_LENGTH);      /* last tag */
457
458     if (!saw_length) {
459         *err = WTAP_ERR_BAD_FILE;
460         *err_info = g_strdup("airopeekv9: record has no length field");
461         return 0;
462     }
463     if (!saw_timestamp_lower) {
464         *err = WTAP_ERR_BAD_FILE;
465         *err_info = g_strdup("airopeekv9: record has no timestamp-lower field");
466         return 0;
467     }
468     if (!saw_timestamp_upper) {
469         *err = WTAP_ERR_BAD_FILE;
470         *err_info = g_strdup("airopeekv9: record has no timestamp-upper field");
471         return 0;
472     }
473
474     return header_len;
475 }
476
477 /*
478  * Time stamps appear to be in nanoseconds since the Windows epoch
479  * as used in FILETIMEs, i.e. midnight, January 1, 1601.
480  *
481  * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
482  * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
483  * that are being subtracted.
484  */
485 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
486
487 static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
488     gint64 *data_offset)
489 {
490     airopeek9_t *airopeek9 = (airopeek9_t *)wth->priv;
491     hdr_info_t hdr_info;
492     int hdrlen;
493     double  t;
494
495     *data_offset = wth->data_offset;
496
497     /* Process the packet header. */
498     hdrlen = airopeekv9_process_header(wth->fh, &hdr_info, err, err_info);
499     if (hdrlen == 0)
500         return FALSE;
501     wth->data_offset += hdrlen;
502
503     /*
504      * If sliceLength is 0, force it to be the actual length of the packet.
505      */
506     if (hdr_info.sliceLength == 0)
507         hdr_info.sliceLength = hdr_info.length;
508
509     if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
510         /*
511          * Probably a corrupt capture file; don't blow up trying
512          * to allocate space for an immensely-large packet.
513          */
514         *err = WTAP_ERR_BAD_FILE;
515         *err_info = g_strdup_printf("airopeek9: File has %u-byte packet, bigger than maximum of %u",
516             hdr_info.sliceLength, WTAP_MAX_PACKET_SIZE);
517         return FALSE;
518     }
519
520     /* fill in packet header length values before slicelength may be
521        adjusted */
522     wth->phdr.len    = hdr_info.length;
523     wth->phdr.caplen = hdr_info.sliceLength;
524
525     /* read the frame data */
526     buffer_assure_space(wth->frame_buffer, hdr_info.sliceLength);
527     wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
528                                   hdr_info.sliceLength, wth->fh, err,
529                                   err_info);
530     wth->data_offset += hdr_info.sliceLength;
531
532     /* recalculate and fill in packet time stamp */
533     t =  (double) hdr_info.timestamp.lower +
534          (double) hdr_info.timestamp.upper * 4294967296.0;
535
536     t *= 1.0e-9;
537     t -= TIME_FIXUP_CONSTANT;
538     wth->phdr.ts.secs  = (time_t) t;
539     wth->phdr.ts.nsecs = (guint32) ((t - wth->phdr.ts.secs)*1000000000);
540
541     switch (wth->file_encap) {
542
543     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
544         /*
545          * The last 4 bytes sometimes contains the FCS but on a lot of
546          * interfaces these are zero.  Is there some way to determine
547          * from the packet header whether it's an FCS or not?
548          *
549          * For now, we just discard those bytes; if we can determine
550          * whether it's an FCS or not, we should use that to determine
551          * whether to supply it as an FCS or discard it.
552          */
553         wth->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
554         if (airopeek9->has_fcs)
555             wth->pseudo_header.ieee_802_11.fcs_len = 4;
556         else {
557             wth->pseudo_header.ieee_802_11.fcs_len = 0;
558             wth->phdr.len -= 4;
559             wth->phdr.caplen -= 4;
560         }
561         break;
562
563     case WTAP_ENCAP_ETHERNET:
564         /*
565          * The last 4 bytes appear to be 0 in the captures I've seen;
566          * are there any captures where it's an FCS?
567          */
568         wth->pseudo_header.eth.fcs_len = 0;
569         wth->phdr.len -= 4;
570         wth->phdr.caplen -= 4;
571         break;
572     }
573
574     return TRUE;
575 }
576
577
578 static gboolean
579 airopeekv9_seek_read(wtap *wth, gint64 seek_off,
580     union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
581     int *err, gchar **err_info)
582 {
583     airopeek9_t *airopeek9 = (airopeek9_t *)wth->priv;
584     hdr_info_t hdr_info;
585
586     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
587         return FALSE;
588
589     /* Process the packet header. */
590     if (airopeekv9_process_header(wth->random_fh, &hdr_info, err, err_info) == -1)
591         return FALSE;
592
593     switch (wth->file_encap) {
594
595     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
596         pseudo_header->ieee_802_11 = hdr_info.ieee_802_11;
597         if (airopeek9->has_fcs)
598             pseudo_header->ieee_802_11.fcs_len = 4;
599         else
600             pseudo_header->ieee_802_11.fcs_len = 0;
601         break;
602
603     case WTAP_ENCAP_ETHERNET:
604         pseudo_header->eth.fcs_len = 0;
605         break;
606     }
607
608     /*
609      * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
610      */
611     errno = WTAP_ERR_CANT_READ;
612     wtap_file_read_expected_bytes(pd, length, wth->random_fh, err, err_info);
613     return TRUE;
614 }