We always HAVE_CONFIG_H so don't bother checking whether we have it or not.
[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/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).
50  */
51
52 /* section header */
53 typedef struct peektagged_section_header {
54         gint8   section_id[4];
55         guint32 section_len;
56         guint32 section_const;
57 } peektagged_section_header_t;
58
59 /*
60  * Network subtype values.
61  *
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?
65  */
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 */
70
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
84
85 /* 64-bit time in nanoseconds from the (Windows FILETIME) epoch */
86 typedef struct peektagged_utime {
87         guint32 upper;
88         guint32 lower;
89 } peektagged_utime;
90
91 typedef struct {
92         gboolean        has_fcs;
93 } peektagged_t;
94
95 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
96     gint64 *data_offset);
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);
100
101 static int wtap_file_read_pattern (wtap *wth, const char *pattern, int *err,
102                                 gchar **err_info)
103 {
104     int c;
105     const char *cp;
106
107     cp = pattern;
108     while (*cp)
109     {
110         c = file_getc(wth->fh);
111         if (c == EOF) {
112             if (file_eof(wth->fh))
113                 return 0;       /* EOF */
114             else {
115                 *err = file_error(wth->fh, err_info);
116                 return -1;      /* error */
117             }
118         }
119         if (c == *cp)
120             cp++;
121         else
122         {
123             if (c == pattern[0])
124                 cp = &pattern[1];
125             else
126                 cp = pattern;
127         }
128     }
129     return (*cp == '\0' ? 1 : 0);
130 }
131
132
133 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
134                                         const char *separators, int *err,
135                                         gchar **err_info)
136 {
137     int c;
138     char *cp;
139     int i;
140
141     for (cp = buffer, i = 0; i < buflen; i++, cp++)
142     {
143         c = file_getc(wth->fh);
144         if (c == EOF) {
145             if (file_eof(wth->fh))
146                 return 0;       /* EOF */
147             else {
148                 *err = file_error(wth->fh, err_info);
149                 return -1;      /* error */
150             }
151         }
152         if (strchr (separators, c) != NULL)
153         {
154             *cp = '\0';
155             break;
156         }
157         else
158             *cp = c;
159     }
160     return i;
161 }
162
163
164 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err,
165                                 gchar **err_info)
166 {
167     int ret;
168     char str_num[12];
169     unsigned long value;
170     char *p;
171
172     ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
173                                          err, err_info);
174     if (ret != 1) {
175         /* 0 means EOF, which means "not a valid Peek tagged file";
176            -1 means error, and "err" has been set. */
177         return ret;
178     }
179     value = strtoul (str_num, &p, 10);
180     if (p == str_num || value > G_MAXUINT32)
181         return 0;
182     *num = (guint32)value;
183     return 1;
184 }
185
186
187 int peektagged_open(wtap *wth, int *err, gchar **err_info)
188 {
189     peektagged_section_header_t ap_hdr;
190     int ret;
191     guint32 fileVersion;
192     guint32 mediaType;
193     guint32 mediaSubType = 0;
194     int file_encap;
195     static const int peektagged_encap[] = {
196         WTAP_ENCAP_ETHERNET,
197         WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
198         WTAP_ENCAP_IEEE_802_11_WITH_RADIO,
199         WTAP_ENCAP_IEEE_802_11_WITH_RADIO
200     };
201     #define NUM_PEEKTAGGED_ENCAPS (sizeof peektagged_encap / sizeof peektagged_encap[0])
202     peektagged_t *peektagged;
203
204     wtap_file_read_unknown_bytes(&ap_hdr, sizeof(ap_hdr), wth->fh, err,
205                                  err_info);
206
207     if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
208         return 0;       /* doesn't begin with a "\177ver" section */
209
210     /*
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).
217      */
218     ret = wtap_file_read_pattern (wth, "<FileVersion>", err, err_info);
219     if (ret != 1) {
220         /* 0 means EOF, which means "not a valid Peek tagged file";
221            -1 means error, and "err" has been set. */
222         return ret;
223     }
224     ret = wtap_file_read_number (wth, &fileVersion, err, err_info);
225     if (ret != 1) {
226         /* 0 means EOF, which means "not a valid Peek tagged file";
227            -1 means error, and "err" has been set. */
228         return ret;
229     }
230
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",
236             fileVersion);
237         return -1;
238     }
239
240     /*
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).
249      */
250     ret = wtap_file_read_pattern (wth, "<MediaType>", err, err_info);
251     if (ret == -1)
252         return -1;
253     if (ret == 0) {
254         *err = WTAP_ERR_BAD_FILE;
255         *err_info = g_strdup("peektagged: <MediaType> tag not found");
256         return -1;
257     }
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);
261     if (ret == -1)
262         return -1;
263     if (ret == 0) {
264         *err = WTAP_ERR_BAD_FILE;
265         *err_info = g_strdup("peektagged: <MediaType> value not found");
266         return -1;
267     }
268
269     ret = wtap_file_read_pattern (wth, "<MediaSubType>", err, err_info);
270     if (ret == -1)
271         return -1;
272     if (ret == 0) {
273         *err = WTAP_ERR_BAD_FILE;
274         *err_info = g_strdup("peektagged: <MediaSubType> tag not found");
275         return -1;
276     }
277     ret = wtap_file_read_number (wth, &mediaSubType, 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: <MediaSubType> value not found");
283         return -1;
284     }
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",
289             mediaSubType);
290         return -1;
291     }
292
293     ret = wtap_file_read_pattern (wth, "pkts", err, err_info);
294     if (ret == -1)
295         return -1;
296     if (ret == 0) {
297         *err = WTAP_ERR_SHORT_READ;
298         return -1;
299     }
300
301     /* skip 8 zero bytes */
302     if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
303         return 0;
304
305     /*
306      * This is an Peek tagged file.
307      */
308     file_encap = peektagged_encap[mediaSubType];
309
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;
315
316     peektagged = (peektagged_t *)g_malloc(sizeof(peektagged_t));
317     wth->priv = (void *)peektagged;
318     switch (mediaSubType) {
319
320     case PEEKTAGGED_NST_ETHERNET:
321     case PEEKTAGGED_NST_802_11:
322     case PEEKTAGGED_NST_802_11_2:
323         peektagged->has_fcs = FALSE;
324         break;
325
326     case PEEKTAGGED_NST_802_11_WITH_FCS:
327         peektagged->has_fcs = TRUE;
328         break;
329     }
330
331     wth->snapshot_length   = 0; /* not available in header */
332
333     return 1;
334 }
335
336 typedef struct {
337     guint32 length;
338     guint32 sliceLength;
339     peektagged_utime timestamp;
340     struct ieee_802_11_phdr ieee_802_11;
341 } hdr_info_t;
342
343 /*
344  * Process the packet header.
345  *
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
350  * are present.
351  */
352 static int
353 peektagged_process_header(FILE_T fh, hdr_info_t *hdr_info, int *err,
354     gchar **err_info)
355 {
356     int header_len = 0;
357     int bytes_read;
358     guint8 tag_value[6];
359     guint16 tag;
360     gboolean saw_length = FALSE;
361     gboolean saw_timestamp_lower = FALSE;
362     gboolean saw_timestamp_upper = FALSE;
363
364     /* Extract the fields from the packet header */
365     do {
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);
371             if (*err == 0) {
372                 if (bytes_read > 0)
373                     *err = WTAP_ERR_SHORT_READ;
374                 else if (bytes_read == 0) {
375                     /*
376                      * Short read if we've read something already;
377                      * just an EOF if we haven't.
378                      */
379                     if (header_len != 0)
380                         *err = WTAP_ERR_SHORT_READ;
381                 }
382             }
383             return 0;
384         }
385         header_len += (int) sizeof(tag_value);
386         tag = pletohs(&tag_value[0]);
387         switch (tag) {
388
389         case TAG_PEEKTAGGED_LENGTH:
390             if (saw_length) {
391                 *err = WTAP_ERR_BAD_FILE;
392                 *err_info = g_strdup("peektagged: record has two length fields");
393                 return 0;
394             }
395             hdr_info->length = pletohl(&tag_value[2]);
396             saw_length = TRUE;
397             break;
398     
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");
403                 return 0;
404             }
405             hdr_info->timestamp.lower = pletohl(&tag_value[2]);
406             saw_timestamp_lower = TRUE;
407             break;
408
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");
413                 return 0;
414             }
415             hdr_info->timestamp.upper = pletohl(&tag_value[2]);
416             saw_timestamp_upper = TRUE;
417             break;
418
419         case TAG_PEEKTAGGED_FLAGS_AND_STATUS:
420             /* XXX - not used yet */
421             break;
422
423         case TAG_PEEKTAGGED_CHANNEL:
424             hdr_info->ieee_802_11.channel = pletohl(&tag_value[2]);
425             break;
426
427         case TAG_PEEKTAGGED_RATE:
428             hdr_info->ieee_802_11.data_rate = pletohl(&tag_value[2]);
429             break;
430
431         case TAG_PEEKTAGGED_SIGNAL_PERC:
432             hdr_info->ieee_802_11.signal_level = pletohl(&tag_value[2]);
433             break;
434
435         case TAG_PEEKTAGGED_SIGNAL_DBM:
436             /* XXX - not used yet */
437             break;
438
439         case TAG_PEEKTAGGED_NOISE_PERC:
440             /* XXX - not used yet */
441             break;
442
443         case TAG_PEEKTAGGED_NOISE_DBM:
444             /* XXX - not used yet */
445             break;
446
447         case TAG_PEEKTAGGED_UNKNOWN_0x000D:
448             /* XXX - seen in an EtherPeek capture; value unknown */
449             break;
450
451         case TAG_PEEKTAGGED_SLICE_LENGTH:
452             hdr_info->sliceLength = pletohl(&tag_value[2]);
453             break;
454
455         default:
456             break;
457         }
458     } while (tag != TAG_PEEKTAGGED_SLICE_LENGTH);       /* last tag */
459
460     if (!saw_length) {
461         *err = WTAP_ERR_BAD_FILE;
462         *err_info = g_strdup("peektagged: record has no length field");
463         return 0;
464     }
465     if (!saw_timestamp_lower) {
466         *err = WTAP_ERR_BAD_FILE;
467         *err_info = g_strdup("peektagged: record has no timestamp-lower field");
468         return 0;
469     }
470     if (!saw_timestamp_upper) {
471         *err = WTAP_ERR_BAD_FILE;
472         *err_info = g_strdup("peektagged: record has no timestamp-upper field");
473         return 0;
474     }
475
476     return header_len;
477 }
478
479 /*
480  * Time stamps appear to be in nanoseconds since the Windows epoch
481  * as used in FILETIMEs, i.e. midnight, January 1, 1601.
482  *
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.
486  */
487 #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60))
488
489 static gboolean peektagged_read(wtap *wth, int *err, gchar **err_info,
490     gint64 *data_offset)
491 {
492     peektagged_t *peektagged = (peektagged_t *)wth->priv;
493     hdr_info_t hdr_info;
494     int hdrlen;
495     double  t;
496
497     *data_offset = file_tell(wth->fh);
498
499     /* Process the packet header. */
500     hdrlen = peektagged_process_header(wth->fh, &hdr_info, err, err_info);
501     if (hdrlen == 0)
502         return FALSE;
503
504     /*
505      * If sliceLength is 0, force it to be the actual length of the packet.
506      */
507     if (hdr_info.sliceLength == 0)
508         hdr_info.sliceLength = hdr_info.length;
509
510     if (hdr_info.sliceLength > WTAP_MAX_PACKET_SIZE) {
511         /*
512          * Probably a corrupt capture file; don't blow up trying
513          * to allocate space for an immensely-large packet.
514          */
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);
518         return FALSE;
519     }
520
521     wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
522
523     /* fill in packet header length values before slicelength may be
524        adjusted */
525     wth->phdr.len    = hdr_info.length;
526     wth->phdr.caplen = hdr_info.sliceLength;
527
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,
532                                   err_info);
533
534     /* recalculate and fill in packet time stamp */
535     t =  (double) hdr_info.timestamp.lower +
536          (double) hdr_info.timestamp.upper * 4294967296.0;
537
538     t *= 1.0e-9;
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);
542
543     switch (wth->file_encap) {
544
545     case WTAP_ENCAP_IEEE_802_11_WITH_RADIO:
546         /*
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?
550          *
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.
554          */
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;
558         else {
559             wth->pseudo_header.ieee_802_11.fcs_len = 0;
560             wth->phdr.len -= 4;
561             wth->phdr.caplen -= 4;
562         }
563         wth->pseudo_header.ieee_802_11.decrypted = FALSE;
564         break;
565
566     case WTAP_ENCAP_ETHERNET:
567         /*
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?
570          */
571         wth->pseudo_header.eth.fcs_len = 0;
572         wth->phdr.len -= 4;
573         wth->phdr.caplen -= 4;
574         break;
575     }
576
577     return TRUE;
578 }
579
580
581 static gboolean
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)
585 {
586     peektagged_t *peektagged = (peektagged_t *)wth->priv;
587     hdr_info_t hdr_info;
588
589     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
590         return FALSE;
591
592     /* Process the packet header. */
593     if (peektagged_process_header(wth->random_fh, &hdr_info, err, err_info) == -1)
594         return FALSE;
595
596     switch (wth->file_encap) {
597
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;
602         else
603             pseudo_header->ieee_802_11.fcs_len = 0;
604         pseudo_header->ieee_802_11.decrypted = FALSE;
605         break;
606
607     case WTAP_ENCAP_ETHERNET:
608         pseudo_header->eth.fcs_len = 0;
609         break;
610     }
611
612     /*
613      * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
614      */
615     errno = WTAP_ERR_CANT_READ;
616     wtap_file_read_expected_bytes(pd, length, wth->random_fh, err, err_info);
617     return TRUE;
618 }