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