file_seek() used to be a wrapper around fseek() or gzseek(), both of
[metze/wireshark/wip.git] / wiretap / radcom.c
1 /* radcom.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <errno.h>
28 #include <string.h>
29 #include "wtap-int.h"
30 #include "file_wrappers.h"
31 #include "buffer.h"
32 #include "radcom.h"
33
34 struct frame_date {
35         guint16 year;
36         guint8  month;
37         guint8  day;
38         guint32 sec;            /* seconds since midnight */
39         guint32 usec;
40 };
41
42 struct unaligned_frame_date {
43         char    year[2];
44         char    month;
45         char    day;
46         char    sec[4];         /* seconds since midnight */
47         char    usec[4];
48 };
49
50 /* Found at the beginning of the file. Bytes 2 and 3 (D2:00) seem to be
51  * different in some captures */
52 static const guint8 radcom_magic[8] = {
53         0x42, 0xD2, 0x00, 0x34, 0x12, 0x66, 0x22, 0x88
54 };
55
56 static const guint8 encap_magic[4] = {
57         0x00, 0x42, 0x43, 0x09
58 };
59
60 static const guint8 active_time_magic[11] = {
61         0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x20, 0x54, 0x69, 0x6d, 0x65
62 };
63
64 /* RADCOM record header - followed by frame data (perhaps including FCS).
65
66    "data_length" appears to be the length of packet data following
67    the record header.  It's 0 in the last record.
68
69    "length" appears to be the amount of captured packet data, and
70    "real_length" might be the actual length of the frame on the wire -
71    in some captures, it's the same as "length", and, in others,
72    it's greater than "length".  In the last record, however, those
73    may have bogus values (or is that some kind of trailer record?).
74
75    "xxx" appears to be all-zero in all but the last record in one
76    capture; if so, perhaps this indicates that the last record is,
77    in fact, a trailer of some sort, and some field in the header
78    is a record type. */
79 struct radcomrec_hdr {
80         char    xxx[4];         /* unknown */
81         char    data_length[2]; /* packet length? */
82         char    xxy[5];         /* unknown */
83         struct unaligned_frame_date date; /* date/time stamp of packet */
84         char    real_length[2]; /* actual length of packet */
85         char    length[2];      /* captured length of packet */
86         char    xxz[2];         /* unknown */
87         char    dce;            /* DCE/DTE flag (and other flags?) */
88         char    xxw[9];         /* unknown */
89 };
90
91 static gboolean radcom_read(wtap *wth, int *err, gchar **err_info,
92         gint64 *data_offset);
93 static gboolean radcom_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 static int radcom_read_rec_header(FILE_T fh, struct radcomrec_hdr *hdr,
97         int *err, gchar **err_info);
98 static gboolean radcom_read_rec_data(FILE_T fh, guint8 *pd, int length,
99         int *err, gchar **err_info);
100
101 int radcom_open(wtap *wth, int *err, gchar **err_info)
102 {
103         int bytes_read;
104         guint8 r_magic[8], t_magic[11], search_encap[7];
105         struct frame_date start_date;
106 #if 0
107         guint32 sec;
108         struct tm tm;
109 #endif
110
111         /* Read in the string that should be at the start of a RADCOM file */
112         errno = WTAP_ERR_CANT_READ;
113         bytes_read = file_read(r_magic, 8, wth->fh);
114         if (bytes_read != 8) {
115                 *err = file_error(wth->fh, err_info);
116                 if (*err != 0)
117                         return -1;
118                 return 0;
119         }
120
121         /* XXX: bytes 2 and 3 of the "magic" header seem to be different in some
122          * captures. We force them to our standard value so that the test
123          * succeeds (until we find if they have a special meaning, perhaps a
124          * version number ?) */
125         r_magic[1] = 0xD2;
126         r_magic[2] = 0x00;
127         if (memcmp(r_magic, radcom_magic, 8) != 0) {
128                 return 0;
129         }
130
131         /* Look for the "Active Time" string. The "frame_date" structure should
132          * be located 32 bytes before the beginning of this string */
133         errno = WTAP_ERR_CANT_READ;
134         bytes_read = file_read(t_magic, 11, wth->fh);
135         if (bytes_read != 11) {
136                 *err = file_error(wth->fh, err_info);
137                 if (*err != 0)
138                         return -1;
139                 return 0;
140         }
141         while (memcmp(t_magic, active_time_magic, 11) != 0)
142         {
143                 if (file_seek(wth->fh, -10, SEEK_CUR, err) == -1)
144                         return -1;
145                 errno = WTAP_ERR_CANT_READ;
146                 bytes_read = file_read(t_magic, 11, wth->fh);
147                 if (bytes_read != 11) {
148                         *err = file_error(wth->fh, err_info);
149                         if (*err != 0)
150                                 return -1;
151                         return 0;
152                 }
153         }
154         if (file_seek(wth->fh, -43, SEEK_CUR, err) == -1) return -1;
155
156         /* Get capture start time */
157         errno = WTAP_ERR_CANT_READ;
158         bytes_read = file_read(&start_date, sizeof(struct frame_date),
159                                wth->fh);
160         if (bytes_read != sizeof(struct frame_date)) {
161                 *err = file_error(wth->fh, err_info);
162                 if (*err != 0)
163                         return -1;
164                 return 0;
165         }
166
167         /* This is a radcom file */
168         wth->file_type = WTAP_FILE_RADCOM;
169         wth->subtype_read = radcom_read;
170         wth->subtype_seek_read = radcom_seek_read;
171         wth->snapshot_length = 0; /* not available in header, only in frame */
172         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
173
174 #if 0
175         tm.tm_year = pletohs(&start_date.year)-1900;
176         tm.tm_mon = start_date.month-1;
177         tm.tm_mday = start_date.day;
178         sec = pletohl(&start_date.sec);
179         tm.tm_hour = sec/3600;
180         tm.tm_min = (sec%3600)/60;
181         tm.tm_sec = sec%60;
182         tm.tm_isdst = -1;
183 #endif
184         if (file_seek(wth->fh, sizeof(struct frame_date), SEEK_CUR, err) == -1)
185                 return -1;
186
187         errno = WTAP_ERR_CANT_READ;
188         bytes_read = file_read(search_encap, 4, wth->fh);
189         if (bytes_read != 4) {
190                 goto read_error;
191         }
192         while (memcmp(encap_magic, search_encap, 4)) {
193                 if (file_seek(wth->fh, -3, SEEK_CUR, err) == -1)
194                         return -1;
195                 errno = WTAP_ERR_CANT_READ;
196                 bytes_read = file_read(search_encap, 4, wth->fh);
197                 if (bytes_read != 4) {
198                         goto read_error;
199                 }
200         }
201         if (file_seek(wth->fh, 12, SEEK_CUR, err) == -1)
202                 return -1;
203         errno = WTAP_ERR_CANT_READ;
204         bytes_read = file_read(search_encap, 4, wth->fh);
205         if (bytes_read != 4) {
206                 goto read_error;
207         }
208         if (memcmp(search_encap, "LAPB", 4) == 0)
209                 wth->file_encap = WTAP_ENCAP_LAPB;
210         else if (memcmp(search_encap, "Ethe", 4) == 0)
211                 wth->file_encap = WTAP_ENCAP_ETHERNET;
212         else if (memcmp(search_encap, "ATM/", 4) == 0)
213                 wth->file_encap = WTAP_ENCAP_ATM_RFC1483;
214         else {
215                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
216                 *err_info = g_strdup_printf("radcom: network type \"%.4s\" unknown", search_encap);
217                 return -1;
218         }
219
220 #if 0
221         bytes_read = file_read(&next_date, sizeof(struct frame_date), wth->fh);
222         errno = WTAP_ERR_CANT_READ;
223         if (bytes_read != sizeof(struct frame_date)) {
224                 goto read_error;
225         }
226
227         while (memcmp(&start_date, &next_date, 4)) {
228                 if (file_seek(wth->fh, 1-sizeof(struct frame_date), SEEK_CUR, err) == -1)
229                         return -1;
230                 errno = WTAP_ERR_CANT_READ;
231                 bytes_read = file_read(&next_date, sizeof(struct frame_date),
232                                    wth->fh);
233                 if (bytes_read != sizeof(struct frame_date)) {
234                         goto read_error;
235                 }
236         }
237 #endif
238
239         if (wth->file_encap == WTAP_ENCAP_ETHERNET) {
240                 if (file_seek(wth->fh, 294, SEEK_CUR, err) == -1)
241                         return -1;
242         } else if (wth->file_encap == WTAP_ENCAP_LAPB) {
243                 if (file_seek(wth->fh, 297, SEEK_CUR, err) == -1)
244                         return -1;
245         } else if (wth->file_encap == WTAP_ENCAP_ATM_RFC1483) {
246                 if (file_seek(wth->fh, 504, SEEK_CUR, err) == -1)
247                         return -1;
248         }
249
250         return 1;
251
252 read_error:
253         *err = file_error(wth->fh, err_info);
254         if (*err != 0)
255                 return -1;
256         return 0;
257 }
258
259 /* Read the next packet */
260 static gboolean radcom_read(wtap *wth, int *err, gchar **err_info,
261                             gint64 *data_offset)
262 {
263         int     ret;
264         struct radcomrec_hdr hdr;
265         guint16 data_length, real_length, length;
266         guint32 sec;
267         int     bytes_read;
268         struct tm tm;
269         guint8  phdr[8];
270         char    fcs[2];
271
272         /* Read record header. */
273         *data_offset = file_tell(wth->fh);
274         ret = radcom_read_rec_header(wth->fh, &hdr, err, err_info);
275         if (ret <= 0) {
276                 /* Read error or EOF */
277                 return FALSE;
278         }
279         data_length = pletohs(&hdr.data_length);
280         if (data_length == 0) {
281                 /*
282                  * The last record appears to have 0 in its "data_length"
283                  * field, but non-zero values in other fields, so we
284                  * check for that and treat it as an EOF indication.
285                  */
286                 *err = 0;
287                 return FALSE;
288         }
289         length = pletohs(&hdr.length);
290         real_length = pletohs(&hdr.real_length);
291
292         if (wth->file_encap == WTAP_ENCAP_LAPB) {
293                 length -= 2; /* FCS */
294                 real_length -= 2;
295         }
296
297         wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
298
299         wth->phdr.len = real_length;
300         wth->phdr.caplen = length;
301
302         tm.tm_year = pletohs(&hdr.date.year)-1900;
303         tm.tm_mon = (hdr.date.month&0x0f)-1;
304         tm.tm_mday = hdr.date.day;
305         sec = pletohl(&hdr.date.sec);
306         tm.tm_hour = sec/3600;
307         tm.tm_min = (sec%3600)/60;
308         tm.tm_sec = sec%60;
309         tm.tm_isdst = -1;
310         wth->phdr.ts.secs = mktime(&tm);
311         wth->phdr.ts.nsecs = pletohl(&hdr.date.usec) * 1000;
312
313         switch (wth->file_encap) {
314
315         case WTAP_ENCAP_ETHERNET:
316                 /* XXX - is there an FCS? */
317                 wth->pseudo_header.eth.fcs_len = -1;
318                 break;
319
320         case WTAP_ENCAP_LAPB:
321                 wth->pseudo_header.x25.flags = (hdr.dce & 0x1) ?
322                     0x00 : FROM_DCE;
323                 break;
324
325         case WTAP_ENCAP_ATM_RFC1483:
326                 /*
327                  * XXX - is this stuff a pseudo-header?
328                  * The direction appears to be in the "hdr.dce" field.
329                  */
330                 if (!radcom_read_rec_data(wth->fh, phdr, sizeof phdr, err,
331                     err_info))
332                         return FALSE;   /* Read error */
333                 length -= 8;
334                 wth->phdr.len -= 8;
335                 wth->phdr.caplen -= 8;
336                 break;
337         }
338
339         /*
340          * Read the packet data.
341          */
342         buffer_assure_space(wth->frame_buffer, length);
343         if (!radcom_read_rec_data(wth->fh,
344             buffer_start_ptr(wth->frame_buffer), length, err, err_info))
345                 return FALSE;   /* Read error */
346
347         if (wth->file_encap == WTAP_ENCAP_LAPB) {
348                 /* Read the FCS.
349                    XXX - should we have some way of indicating the
350                    presence and size of an FCS to our caller?
351                    That'd let us handle other file types as well. */
352                 errno = WTAP_ERR_CANT_READ;
353                 bytes_read = file_read(&fcs, sizeof fcs, wth->fh);
354                 if (bytes_read != sizeof fcs) {
355                         *err = file_error(wth->fh, err_info);
356                         if (*err == 0)
357                                 *err = WTAP_ERR_SHORT_READ;
358                         return FALSE;
359                 }
360         }
361
362         return TRUE;
363 }
364
365 static gboolean
366 radcom_seek_read(wtap *wth, gint64 seek_off,
367                  union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
368                  int *err, gchar **err_info)
369 {
370         int     ret;
371         struct radcomrec_hdr hdr;
372         guint8  phdr[8];
373
374         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
375                 return FALSE;
376
377         /* Read record header. */
378         ret = radcom_read_rec_header(wth->random_fh, &hdr, err, err_info);
379         if (ret <= 0) {
380                 /* Read error or EOF */
381                 if (ret == 0) {
382                         /* EOF means "short read" in random-access mode */
383                         *err = WTAP_ERR_SHORT_READ;
384                 }
385                 return FALSE;
386         }
387
388         switch (wth->file_encap) {
389
390         case WTAP_ENCAP_ETHERNET:
391                 /* XXX - is there an FCS? */
392                 pseudo_header->eth.fcs_len = -1;
393                 break;
394
395         case WTAP_ENCAP_LAPB:
396                 pseudo_header->x25.flags = (hdr.dce & 0x1) ? 0x00 : FROM_DCE;
397                 break;
398
399         case WTAP_ENCAP_ATM_RFC1483:
400                 /*
401                  * XXX - is this stuff a pseudo-header?
402                  * The direction appears to be in the "hdr.dce" field.
403                  */
404                 if (!radcom_read_rec_data(wth->random_fh, phdr, sizeof phdr,
405                     err, err_info))
406                         return FALSE;   /* Read error */
407                 break;
408         }
409
410         /*
411          * Read the packet data.
412          */
413         return radcom_read_rec_data(wth->random_fh, pd, length, err, err_info);
414 }
415
416 static int
417 radcom_read_rec_header(FILE_T fh, struct radcomrec_hdr *hdr, int *err,
418                        gchar **err_info)
419 {
420         int     bytes_read;
421
422         errno = WTAP_ERR_CANT_READ;
423         bytes_read = file_read(hdr, sizeof *hdr, fh);
424         if (bytes_read != sizeof *hdr) {
425                 *err = file_error(fh, err_info);
426                 if (*err != 0)
427                         return -1;
428                 if (bytes_read != 0) {
429                         *err = WTAP_ERR_SHORT_READ;
430                         return -1;
431                 }
432                 return 0;
433         }
434         return 1;
435 }
436
437 static gboolean
438 radcom_read_rec_data(FILE_T fh, guint8 *pd, int length, int *err,
439                      gchar **err_info)
440 {
441         int     bytes_read;
442
443         errno = WTAP_ERR_CANT_READ;
444         bytes_read = file_read(pd, length, fh);
445
446         if (bytes_read != length) {
447                 *err = file_error(fh, err_info);
448                 if (*err == 0)
449                         *err = WTAP_ERR_SHORT_READ;
450                 return FALSE;
451         }
452         return TRUE;
453 }