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