Check for errors and EOF, and handle them appropriately; don't treat all
[obnox/wireshark/wip.git] / wiretap / airopeek9.c
1 /* airopeek9.c
2  * Routines for opening AiroPeek V9 files
3  *
4  * $Id: airopeek9.c,v 1.2 2003/12/02 20:27:14 guy Exp $
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 /* section header */
43 typedef struct airopeek_section_header {
44         gint8   section_id[4];
45         guint32 section_len;
46         guint32 section_const;
47 } airopeek_section_header_t;
48
49 #define AIROPEEK_V9_LENGTH_OFFSET               2
50 #define AIROPEEK_V9_TIMESTAMP_LOWER_OFFSET      8
51 #define AIROPEEK_V9_TIMESTAMP_UPPER_OFFSET      14
52 #define AIROPEEK_V9_FLAGS_OFFSET                20
53 #define AIROPEEK_V9_STATUS_OFFSET               22
54 #define AIROPEEK_V9_CHANNEL_OFFSET              26
55 #define AIROPEEK_V9_RATE_OFFSET                 32
56 #define AIROPEEK_V9_SIGNAL_PERC_OFFSET          38
57 #define AIROPEEK_V9_SIGNAL_DBM_OFFSET           44
58 #define AIROPEEK_V9_NOISE_PERC_OFFSET           50
59 #define AIROPEEK_V9_NOISE_DBM_OFFSET            56
60 #define AIROPEEK_V9_SLICE_LENGTH_OFFSET         62
61
62 #define AIROPEEK_V9_PKT_SIZE                    66
63
64 /* 64-bit time in nano seconds from the (Mac) epoch */
65 typedef struct airopeek_utime {
66         guint32 upper;
67         guint32 lower;
68 } airopeek_utime;
69
70 static const unsigned int mac2unix = 2082844800u;
71
72 static gboolean airopeek_read_v9(wtap *wth, int *err, long *data_offset);
73 static gboolean airopeek_seek_read_v9(wtap *wth, long seek_off,
74     union wtap_pseudo_header *pseudo_header, guchar *pd, int length, int *err);
75
76 static int wtap_file_read_pattern (wtap *wth, char *pattern, int *err)
77 {
78     int c;
79     char *cp;
80
81     cp = pattern;
82     while (*cp)
83     {
84         c = file_getc(wth->fh);
85         if (c == EOF) {
86             if (file_eof(wth->fh))
87                 return 0;       /* EOF */
88             else {
89                 /* We (presumably) got an error (there's no equivalent to
90                    "ferror()" in zlib, alas, so we don't have a wrapper
91                    to check for an error). */
92                 *err = file_error(wth->fh);
93                 return -1;      /* error */
94             }
95         }
96         if (c == *cp)
97             cp++;
98         else
99         {
100             if (c == pattern[0])
101                 cp = &pattern[1];
102             else
103                 cp = pattern;
104         }
105     }
106     return (*cp == '\0' ? 1 : 0);
107 }
108
109
110 static int wtap_file_read_till_separator (wtap *wth, char *buffer, int buflen,
111                                         char *separators, int *err)
112 {
113     int c;
114     char *cp;
115     int i;
116
117     for (cp = buffer, i = 0; i < buflen; i++, cp++)
118     {
119         c = file_getc(wth->fh);
120         if (c == EOF) {
121             if (file_eof(wth->fh))
122                 return 0;       /* EOF */
123             else {
124                 /* We (presumably) got an error (there's no equivalent to
125                    "ferror()" in zlib, alas, so we don't have a wrapper
126                    to check for an error). */
127                 *err = file_error(wth->fh);
128                 return -1;      /* error */
129             }
130         }
131         if (strchr (separators, c))
132         {
133             *cp = '\0';
134             break;
135         }
136         else
137             *cp = c;
138     }
139     return i;
140 }
141
142
143 static int wtap_file_read_number (wtap *wth, guint32 *num, int *err)
144 {
145     int ret;
146     char str_num[12];
147     unsigned long long value;
148     char *p;
149
150     ret = wtap_file_read_till_separator (wth, str_num, sizeof (str_num)-1, "<",
151                                          err);
152     if (ret != 1) {
153         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
154            -1 means error, and "err" has been set. */
155         return ret;
156     }
157     value = strtoul (str_num, &p, 10);
158     if (p == str_num || value > UINT_MAX)
159         return 0;
160     *num = value;
161     return 1;
162 }
163
164
165 int airopeek9_open(wtap *wth, int *err)
166 {
167     airopeek_section_header_t ap_hdr;
168     int ret;
169     guint32 fileVersion;
170     guint32 mediaType;
171     int file_encap;
172
173     wtap_file_read_unknown_bytes(&ap_hdr, sizeof(ap_hdr), wth->fh, err);
174
175     if (memcmp (ap_hdr.section_id, "\177ver", sizeof(ap_hdr.section_id)) != 0)
176         return 0;       /* doesn't begin with a "\177ver" section */
177
178     /*
179      * XXX - we should get the length of the "\177ver" section, check
180      * that it's followed by a little-endian 0x00000200, and then,
181      * when reading the XML, make sure we don't go past the end of
182      * that section, and skip to the end of tha section when
183      * we have the file version (and possibly check to make sure all
184      * tags are properly opened and closed).
185      */
186     ret = wtap_file_read_pattern (wth, "<FileVersion>", err);
187     if (ret != 1) {
188         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
189            -1 means error, and "err" has been set. */
190         return ret;
191     }
192     ret = wtap_file_read_number (wth, &fileVersion, err);
193     if (ret != 1) {
194         /* 0 means EOF, which means "not a valid AiroPeek V9 file";
195            -1 means error, and "err" has been set. */
196         return ret;
197     }
198
199     /* If we got this far, we assume it's an AiroPeek V9 file. */
200     if (fileVersion != 9) {
201         /* We only support version 9 and later. */
202         g_message("airopeekv9: version %u unsupported", fileVersion);
203         *err = WTAP_ERR_UNSUPPORTED;
204         return -1;
205     }
206
207     /*
208      * XXX - once we've skipped the "\177ver" section, we should
209      * check for a "sess" section and fail if we don't see it.
210      * Then we should get the length of the "sess" section, check
211      * that it's followed by a little-endian 0x00000200, and then,
212      * when reading the XML, make sure we don't go past the end of
213      * that section, and skip to the end of tha section when
214      * we have the file version (and possibly check to make sure all
215      * tags are properly opened and closed).
216      */
217     ret = wtap_file_read_pattern (wth, "<MediaType>", err);
218     if (ret == -1)
219         return -1;
220     if (ret == 0) {
221         g_message("airopeekv9: <MediaType> tag not found");
222         *err = WTAP_ERR_UNSUPPORTED;
223         return -1;
224     }
225     /* XXX - this appears to be 0, which is also the media type for
226        802.11 in the older AiroPeek format; should we require it to be
227        0?  And should we check the MediaSubType value as well? */
228     ret = wtap_file_read_number (wth, &mediaType, err);
229     if (ret == -1)
230         return -1;
231     if (ret == 0) {
232         g_message("airopeekv9: <MediaType> value not found");
233         *err = WTAP_ERR_UNSUPPORTED;
234         return -1;
235     }
236
237     ret = wtap_file_read_pattern (wth, "pkts", err);
238     if (ret == -1)
239         return -1;
240     if (ret == 0) {
241         *err = WTAP_ERR_SHORT_READ;
242         return -1;
243     }
244
245     /* skip 8 zero bytes */
246     if (file_seek (wth->fh, 8L, SEEK_CUR, err) == -1)
247         return 0;
248
249     /*
250      * This is an AiroPeek V9 file.
251      */
252
253     wth->data_offset = file_tell (wth->fh);
254
255     file_encap = WTAP_ENCAP_IEEE_802_11_WITH_RADIO;
256
257     wth->file_type = WTAP_FILE_AIROPEEK_V9;
258     wth->file_encap = file_encap;
259     wth->subtype_read = airopeek_read_v9;
260     wth->subtype_seek_read = airopeek_seek_read_v9;
261
262     wth->snapshot_length   = 0; /* not available in header */
263
264     return 1;
265 }
266
267 static gboolean airopeek_read_v9(wtap *wth, int *err, long *data_offset)
268 {
269     guchar ap_pkt[AIROPEEK_V9_PKT_SIZE];
270     guint32 length;
271     guint32 sliceLength;
272     airopeek_utime timestamp;
273     double  t;
274
275     *data_offset = wth->data_offset;
276
277     wtap_file_read_expected_bytes(ap_pkt, sizeof(ap_pkt), wth->fh, err);
278     wth->data_offset += sizeof(ap_pkt);
279
280     /* Extract the fields from the packet */
281     length = pletohl(&ap_pkt[AIROPEEK_V9_LENGTH_OFFSET]);
282     sliceLength = pletohl(&ap_pkt[AIROPEEK_V9_SLICE_LENGTH_OFFSET]);
283     timestamp.upper = pletohl(&ap_pkt[AIROPEEK_V9_TIMESTAMP_UPPER_OFFSET]);
284     timestamp.lower = pletohl(&ap_pkt[AIROPEEK_V9_TIMESTAMP_LOWER_OFFSET]);
285
286     /* force sliceLength to be the actual length of the packet */
287     if (sliceLength == 0) {
288         sliceLength = length;
289     }
290
291     /* fill in packet header length values before slicelength may be
292        adjusted */
293     wth->phdr.len    = length;
294     wth->phdr.caplen = sliceLength;
295
296     /*
297      * Fill the pseudo header with radio information.
298      * XXX - we should supply the additional information;
299      * the pseudo-header should probably be supplied in a fashion
300      * similar to the new BSD radio header, so that the 802.11
301      * dissector can determine which, if any, information items
302      * are present.
303      */
304     wth->pseudo_header.ieee_802_11.channel =
305             pletohl(&ap_pkt[AIROPEEK_V9_CHANNEL_OFFSET]);
306     wth->pseudo_header.ieee_802_11.data_rate =
307             pletohl(&ap_pkt[AIROPEEK_V9_RATE_OFFSET]);
308     wth->pseudo_header.ieee_802_11.signal_level =
309             pletohl(&ap_pkt[AIROPEEK_V9_SIGNAL_PERC_OFFSET]);
310
311     /* read the frame data */
312     buffer_assure_space(wth->frame_buffer, sliceLength);
313     wtap_file_read_expected_bytes(buffer_start_ptr(wth->frame_buffer),
314                                   sliceLength, wth->fh, err);
315     wth->data_offset += sliceLength;
316
317     /* recalculate and fill in packet time stamp */
318     t =  (double) timestamp.lower +
319          (double) timestamp.upper * 4294967296.0;
320
321     t = t / 1000.0;     /* nano seconds -> micro seconds */
322     t -= (double) mac2unix * 1000000.0;
323     wth->phdr.ts.tv_sec  = (time_t)  (t/1000000.0);
324     wth->phdr.ts.tv_usec = (guint32) (t - (double) wth->phdr.ts.tv_sec *
325                                                    1000000.0);
326
327     /*
328      * The last 4 bytes sometimes contains the FCS but on a lot of
329      * interfaces these are zero. To eleminate problems we reduce
330      * the length by 4.
331      *
332      * XXX - is there any way to find out whether it's an FCS or not?
333      */
334     wth->phdr.len -= 4;
335     wth->phdr.caplen -= 4;
336
337     wth->phdr.pkt_encap = wth->file_encap;
338     return TRUE;
339 }
340
341
342 static gboolean
343 airopeek_seek_read_v9(wtap *wth, long seek_off,
344     union wtap_pseudo_header *pseudo_header, guchar *pd, int length, int *err)
345 {
346     guchar ap_pkt[AIROPEEK_V9_PKT_SIZE];
347
348     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
349         return FALSE;
350
351     /* Read the packet header. */
352     wtap_file_read_expected_bytes(ap_pkt, sizeof(ap_pkt), wth->random_fh, err);
353
354     pseudo_header->ieee_802_11.channel =
355             pletohl(&ap_pkt[AIROPEEK_V9_CHANNEL_OFFSET]);
356     pseudo_header->ieee_802_11.data_rate =
357             pletohl(&ap_pkt[AIROPEEK_V9_RATE_OFFSET]);
358     pseudo_header->ieee_802_11.signal_level =
359             pletohl(&ap_pkt[AIROPEEK_V9_SIGNAL_PERC_OFFSET]);
360
361     /*
362      * XXX - should "errno" be set in "wtap_file_read_expected_bytes()"?
363      */
364     errno = WTAP_ERR_CANT_READ;
365     wtap_file_read_expected_bytes(pd, length, wth->random_fh, err);
366     return TRUE;
367 }