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