Improve the heuristic for Frame Relay, and move that heuristic after the
[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 /* network subtype values */
56 #define AIROPEEK_V9_NST_ETHERNET                0
57 #define AIROPEEK_V9_NST_802_11                  1       /* 802.11 with 0's at the end */
58 #define AIROPEEK_V9_NST_802_11_WITH_FCS         3       /* 802.11 with FCS at the end */
59
60 /* tags for fields in packet header */
61 #define TAG_AIROPEEK_V9_LENGTH                  0x0000
62 #define TAG_AIROPEEK_V9_TIMESTAMP_LOWER         0x0001
63 #define TAG_AIROPEEK_V9_TIMESTAMP_UPPER         0x0002
64 #define TAG_AIROPEEK_V9_FLAGS_AND_STATUS        0x0003
65 #define TAG_AIROPEEK_V9_CHANNEL                 0x0004
66 #define TAG_AIROPEEK_V9_RATE                    0x0005
67 #define TAG_AIROPEEK_V9_SIGNAL_PERC             0x0006
68 #define TAG_AIROPEEK_V9_SIGNAL_DBM              0x0007
69 #define TAG_AIROPEEK_V9_NOISE_PERC              0x0008
70 #define TAG_AIROPEEK_V9_NOISE_DBM               0x0009
71 #define TAG_AIROPEEK_V9_UNKNOWN_0x000D          0x000D
72 #define TAG_AIROPEEK_V9_SLICE_LENGTH            0xffff
73
74 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
75 typedef struct airopeek_utime {
76         guint32 upper;
77         guint32 lower;
78 } airopeek_utime;
79
80 static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
81     long *data_offset);
82 static gboolean airopeekv9_seek_read(wtap *wth, long seek_off,
83     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
84     int *err, gchar **err_info);
85 static void airopeekv9_close(wtap *wth);
86
87 static int wtap_file_read_pattern (wtap *wth, char *pattern, int *err)
88 {
89     int c;
90     char *cp;
91
92     cp = pattern;
93     while (*cp)
94     {
95         c = file_getc(wth->fh);
96         if (c == EOF) {
97             if (file_eof(wth->fh))
98                 return 0;       /* EOF */
99             else {
100                 /* We (presumably) got an error (there's no equivalent to
101                    "ferror()" in zlib, alas, so we don't have a wrapper
102                    to check for an error). */
103                 *err = file_error(wth->fh);
104                 return -1;      /* error */
105             }
106         }
107         if (c == *cp)
108             cp++;
109         else
110         {
111             if (c == pattern[0])
112                 cp = &pattern[1];
113             else
114                 cp = pattern;
115         }
116     }
117     return (*cp == '\0' ? 1 : 0);
118 }
119
120
121 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
122                                         char *separators, int *err)
123 {
124     int c;
125     char *cp;
126     int i;
127
128     for (cp = buffer, i = 0; i < buflen; i++, cp++)
129     {
130         c = file_getc(wth->fh);
131         if (c == EOF) {
132             if (file_eof(wth->fh))
133                 return 0;       /* EOF */
134             else {
135                 /* We (presumably) got an error (there's no equivalent to
136                    "ferror()" in zlib, alas, so we don't have a wrapper
137                    to check for an error). */
138                 *err = file_error(wth->fh);
139                 return -1;      /* error */
140             }
141         }
142         if (strchr (separators, c))
143         {
144             *cp = '\0';
145             break;
146         }
147         else
148             *cp = c;
149     }
150     return i;
151 }
152
153
154 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err)
155 {
156     int ret;
157     char str_num[12];
158     unsigned long value;
159     char *p;
160
161     ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
162                                          err);
163     if (ret != 1) {
164         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
165            -1 means error, and "err" has been set. */
166         return ret;
167     }
168     value = strtoul (str_num, &p, 10);
169     if (p == str_num || value > UINT_MAX)
170         return 0;
171     *num = value;
172     return 1;
173 }
174
175
176 int airopeek9_open(wtap *wth, int *err, gchar **err_info)
177 {
178     airopeek_section_header_t ap_hdr;
179     int ret;
180     guint32 fileVersion;
181     guint32 mediaType;
182     guint32 mediaSubType;
183     int file_encap;
184     static const int airopeek9_encap[] = {
185         WTAP_ENCAP_ETHERNET,
186         WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
187         WTAP_ENCAP_UNKNOWN,
188         WTAP_ENCAP_IEEE_802_11_WITH_RADIO
189     };
190     #define NUM_AIROPEEK9_ENCAPS (sizeof airopeek9_encap / sizeof airopeek9_encap[0])
191
192     wtap_file_read_unknown_bytes(&ap_hdr, sizeof(ap_hdr), wth->fh, err);
193
194     if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
195         return 0;       /* doesn't begin with a "\177ver" section */
196
197     /*
198      * XXX - we should get the length of the "\177ver" section, check
199      * that it's followed by a little-endian 0x00000200, and then,
200      * when reading the XML, make sure we don't go past the end of
201      * that section, and skip to the end of that section when
202      * we have the file version (and possibly check to make sure all
203      * tags are properly opened and closed).
204      */
205     ret = wtap_file_read_pattern (wth, "<FileVersion>", err);
206     if (ret != 1) {
207         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
208            -1 means error, and "err" has been set. */
209         return ret;
210     }
211     ret = wtap_file_read_number (wth, &fileVersion, err);
212     if (ret != 1) {
213         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
214            -1 means error, and "err" has been set. */
215         return ret;
216     }
217
218     /* If we got this far, we assume it's an AiroPeek V9 file. */
219     if (fileVersion != 9) {
220         /* We only support version 9. */
221         *err = WTAP_ERR_UNSUPPORTED;
222         *err_info = g_strdup_printf("airopeekv9: version %u unsupported",
223             fileVersion);
224         return -1;
225     }
226
227     /*
228      * XXX - once we've skipped the "\177ver" section, we should
229      * check for a "sess" section and fail if we don't see it.
230      * Then we should get the length of the "sess" section, check
231      * that it's followed by a little-endian 0x00000200, and then,
232      * when reading the XML, make sure we don't go past the end of
233      * that section, and skip to the end of the section when
234      * we have the file version (and possibly check to make sure all
235      * tags are properly opened and closed).
236      */
237     ret = wtap_file_read_pattern (wth, "<MediaType>", err);
238     if (ret == -1)
239         return -1;
240     if (ret == 0) {
241         *err = WTAP_ERR_UNSUPPORTED;
242         *err_info = g_strdup("airopeekv9: <MediaType> tag not found");
243         return -1;
244     }
245     /* XXX - this appears to be 0 in both the EtherPeek and AiroPeek
246        files we've seen; should we require it to be 0? */
247     ret = wtap_file_read_number (wth, &mediaType, err);
248     if (ret == -1)
249         return -1;
250     if (ret == 0) {
251         *err = WTAP_ERR_UNSUPPORTED;
252         *err_info = g_strdup("airopeekv9: <MediaType> value not found");
253         return -1;
254     }
255
256     ret = wtap_file_read_pattern (wth, "<MediaSubType>", err);
257     if (ret == -1)
258         return -1;
259     if (ret == 0) {
260         *err = WTAP_ERR_UNSUPPORTED;
261         *err_info = g_strdup("airopeekv9: <MediaSubType> tag not found");
262         return -1;
263     }
264     ret = wtap_file_read_number (wth, &mediaSubType, err);
265     if (ret == -1)
266         return -1;
267     if (ret == 0) {
268         *err = WTAP_ERR_UNSUPPORTED;
269         *err_info = g_strdup("airopeekv9: <MediaSubType> value not found");
270         return -1;
271     }
272     if (mediaSubType >= NUM_AIROPEEK9_ENCAPS
273         || airopeek9_encap[mediaSubType] == WTAP_ENCAP_UNKNOWN) {
274         *err = WTAP_ERR_UNSUPPORTED_ENCAP;
275         *err_info = g_strdup_printf("airopeekv9: network type %u unknown or unsupported",
276             mediaSubType);
277         return -1;
278     }
279
280     ret = wtap_file_read_pattern (wth, "pkts", err);
281     if (ret == -1)
282         return -1;
283     if (ret == 0) {
284         *err = WTAP_ERR_SHORT_READ;
285         return -1;
286     }
287
288     /* skip 8 zero bytes */
289     if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
290         return 0;
291
292     /*
293      * This is an EtherPeek or AiroPeek V9 file.
294      */
295     wth->data_offset = file_tell (wth->fh);
296
297     file_encap = airopeek9_encap[mediaSubType];
298
299     wth->file_type = WTAP_FILE_AIROPEEK_V9;
300     wth->file_encap = file_encap;
301     wth->subtype_read = airopeekv9_read;
302     wth->subtype_seek_read = airopeekv9_seek_read;
303     wth->subtype_close = airopeekv9_close;
304
305     wth->capture.airopeek9 = g_malloc(sizeof(airopeek9_t));
306     switch (mediaSubType) {
307
308     case AIROPEEK_V9_NST_ETHERNET:
309     case AIROPEEK_V9_NST_802_11:
310         wth->capture.airopeek9->has_fcs = FALSE;
311         break;
312
313     case AIROPEEK_V9_NST_802_11_WITH_FCS:
314         wth->capture.airopeek9->has_fcs = TRUE;
315         break;
316     }
317
318     wth->snapshot_length   = 0; /* not available in header */
319
320     return 1;
321 }
322
323 typedef struct {
324     guint32 length;
325     guint32 sliceLength;
326     airopeek_utime timestamp;
327     struct ieee_802_11_phdr ieee_802_11;
328 } hdr_info_t;
329
330 /*
331  * Process the packet header.
332  *
333  * XXX - we should supply the additional radio information;
334  * the pseudo-header should probably be supplied in a fashion
335  * similar to the new BSD radio header, so that the 802.11
336  * dissector can determine which, if any, information items
337  * are present.
338  */
339 static int
340 airopeekv9_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err,
341     gchar **err_info)
342 {
343     long header_len = 0;
344     int bytes_read;
345     guint8 tag_value[6];
346     guint16 tag;
347     gboolean saw_length = FALSE;
348     gboolean saw_timestamp_lower = FALSE;
349     gboolean saw_timestamp_upper = FALSE;
350
351     /* Extract the fields from the packet header */
352     do {
353         /* Get the tag and value.
354            XXX - this assumes all values are 4 bytes long. */
355         bytes_read = file_read(tag_value, 1, sizeof tag_value, fh);
356         if (bytes_read != (int) sizeof tag_value) {
357             *err = file_error(fh);
358             if (*err == 0) {
359                 if (bytes_read > 0)
360                     *err = WTAP_ERR_SHORT_READ;
361                 else if (bytes_read == 0) {
362                     /*
363                      * Short read if we've read something already;
364                      * just an EOF if we haven't.
365                      */
366                     if (header_len != 0)
367                         *err = WTAP_ERR_SHORT_READ;
368                 }
369             }
370             return -1;
371         }
372         header_len += sizeof(tag_value);
373         tag = pletohs(&tag_value[0]);
374         switch (tag) {
375
376         case TAG_AIROPEEK_V9_LENGTH:
377             if (saw_length) {
378                 *err = WTAP_ERR_BAD_RECORD;
379                 *err_info = g_strdup("airopeekv9: record has two length fields");
380                 return FALSE;
381             }
382             hdr_info->length = pletohl(&tag_value[2]);
383             saw_length = TRUE;
384             break;
385     
386         case TAG_AIROPEEK_V9_TIMESTAMP_LOWER:
387             if (saw_timestamp_lower) {
388                 *err = WTAP_ERR_BAD_RECORD;
389                 *err_info = g_strdup("airopeekv9: record has two timestamp-lower fields");
390                 return FALSE;
391             }
392             hdr_info->timestamp.lower = pletohl(&tag_value[2]);
393             saw_timestamp_lower = TRUE;
394             break;
395
396         case TAG_AIROPEEK_V9_TIMESTAMP_UPPER:
397             if (saw_timestamp_upper) {
398                 *err = WTAP_ERR_BAD_RECORD;
399                 *err_info = g_strdup("airopeekv9: record has two timestamp-upper fields");
400                 return FALSE;
401             }
402             hdr_info->timestamp.upper = pletohl(&tag_value[2]);
403             saw_timestamp_upper = TRUE;
404             break;
405
406         case TAG_AIROPEEK_V9_FLAGS_AND_STATUS:
407             /* XXX - not used yet */
408             break;
409
410         case TAG_AIROPEEK_V9_CHANNEL:
411             hdr_info->ieee_802_11.channel = pletohl(&tag_value[2]);
412             break;
413
414         case TAG_AIROPEEK_V9_RATE:
415             hdr_info->ieee_802_11.data_rate = pletohl(&tag_value[2]);
416             break;
417
418         case TAG_AIROPEEK_V9_SIGNAL_PERC:
419             hdr_info->ieee_802_11.signal_level = pletohl(&tag_value[2]);
420             break;
421
422         case TAG_AIROPEEK_V9_SIGNAL_DBM:
423             /* XXX - not used yet */
424             break;
425
426         case TAG_AIROPEEK_V9_NOISE_PERC:
427             /* XXX - not used yet */
428             break;
429
430         case TAG_AIROPEEK_V9_NOISE_DBM:
431             /* XXX - not used yet */
432             break;
433
434         case TAG_AIROPEEK_V9_UNKNOWN_0x000D:
435             /* XXX - seen in an EtherPeek capture; value unknown */
436             break;
437
438         case TAG_AIROPEEK_V9_SLICE_LENGTH:
439             hdr_info->sliceLength = pletohl(&tag_value[2]);
440             break;
441
442         default:
443             break;
444         }
445     } while (tag != TAG_AIROPEEK_V9_SLICE_LENGTH);      /* last tag */
446
447     if (!saw_length) {
448         *err = WTAP_ERR_BAD_RECORD;
449         *err_info = g_strdup("airopeekv9: record has no length field");
450         return FALSE;
451     }
452     if (!saw_timestamp_lower) {
453         *err = WTAP_ERR_BAD_RECORD;
454         *err_info = g_strdup("airopeekv9: record has no timestamp-lower field");
455         return FALSE;
456     }
457     if (!saw_timestamp_upper) {
458         *err = WTAP_ERR_BAD_RECORD;
459         *err_info = g_strdup("airopeekv9: record has no timestamp-upper field");
460         return FALSE;
461     }
462
463     return header_len;
464 }
465
466 /*
467  * Time stamps appear to be in nanoseconds since the Windows epoch
468  * as used in FILETIMEs, i.e. midnight, January 1, 1601.
469  *
470  * This magic number came from "nt_time_to_nstime()" in "packet-smb.c".
471  * 1970-1601 is 369; I'm not sure what the extra 3 days and 6 hours are
472  8 that are being subtracted.
473  */
474 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
475
476 static gboolean airopeekv9_read(wtap *wth, int *err, gchar **err_info,
477     long *data_offset)
478 {
479     hdr_info_t hdr_info;
480     int hdrlen;
481     double  t;
482
483     *data_offset = wth->data_offset;
484
485     /* Process the packet header. */
486     hdrlen = airopeekv9_process_header(wth->fh, &hdr_info, err, err_info);
487     if (hdrlen == -1)
488         return FALSE;
489     wth->data_offset += hdrlen;
490
491     /* force sliceLength to be the actual length of the packet */
492     if (hdr_info.sliceLength == 0)
493         hdr_info.sliceLength = hdr_info.length;
494
495     /* fill in packet header length values before slicelength may be
496        adjusted */
497     wth->phdr.len    = hdr_info.length;
498     wth->phdr.caplen = hdr_info.sliceLength;
499
500     /* read the frame data */
501     buffer_assure_space(wth->frame_buffer, hdr_info.sliceLength);
502     wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
503                                   hdr_info.sliceLength, wth->fh, err);
504     wth->data_offset += hdr_info.sliceLength;
505
506     /* recalculate and fill in packet time stamp */
507     t =  (double) hdr_info.timestamp.lower +
508          (double) hdr_info.timestamp.upper * 4294967296.0;
509
510     t *= 1.0e-9;
511     t -= TIME_FIXUP_CONSTANT;
512     wth->phdr.ts.tv_sec  = (time_t) t;
513     wth->phdr.ts.tv_usec = (guint32) ((t - wth->phdr.ts.tv_sec)*1000000);
514
515     switch (wth->file_encap) {
516
517     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
518         /*
519          * The last 4 bytes sometimes contains the FCS but on a lot of
520          * interfaces these are zero.  Is there some way to determine
521          * from the packet header whether it's an FCS or not?
522          *
523          * For now, we just discard those bytes; if we can determine
524          * whether it's an FCS or not, we should use that to determine
525          * whether to supply it as an FCS or discard it.
526          */
527         wth->pseudo_header.ieee_802_11 = hdr_info.ieee_802_11;
528         if (wth->capture.airopeek9->has_fcs)
529             wth->pseudo_header.ieee_802_11.fcs_len = 4;
530         else {
531             wth->pseudo_header.ieee_802_11.fcs_len = 0;
532             wth->phdr.len -= 4;
533             wth->phdr.caplen -= 4;
534         }
535         break;
536
537     case WTAP_ENCAP_ETHERNET:
538         /*
539          * The last 4 bytes appear to be 0 in the captures I've seen;
540          * are there any captures where it's an FCS?
541          */
542         wth->pseudo_header.eth.fcs_len = 0;
543         wth->phdr.len -= 4;
544         wth->phdr.caplen -= 4;
545         break;
546     }
547
548     return TRUE;
549 }
550
551
552 static gboolean
553 airopeekv9_seek_read(wtap *wth, long seek_off,
554     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
555     int *err, gchar **err_info)
556 {
557     hdr_info_t hdr_info;
558
559     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
560         return FALSE;
561
562     /* Process the packet header. */
563     if (airopeekv9_process_header(wth->random_fh, &hdr_info, err, err_info) == -1)
564         return FALSE;
565
566     switch (wth->file_encap) {
567
568     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
569         pseudo_header->ieee_802_11 = hdr_info.ieee_802_11;
570         if (wth->capture.airopeek9->has_fcs)
571             pseudo_header->ieee_802_11.fcs_len = 4;
572         else
573             pseudo_header->ieee_802_11.fcs_len = 0;
574         break;
575
576     case WTAP_ENCAP_ETHERNET:
577         pseudo_header->eth.fcs_len = 0;
578         break;
579     }
580
581     /*
582      * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
583      */
584     errno = WTAP_ERR_CANT_READ;
585     wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);
586     return TRUE;
587 }
588
589 static void
590 airopeekv9_close(wtap *wth)
591 {
592     g_free(wth->capture.airopeek9);
593 }