a4fe9b30e6a21cf0c241617903b323e2d996d07f
[obnox/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         wth->data_offset = 8;
134         errno = WTAP_ERR_CANT_READ;
135         bytes_read = file_read(t_magic, 11, wth->fh);
136         if (bytes_read != 11) {
137                 *err = file_error(wth->fh, err_info);
138                 if (*err != 0)
139                         return -1;
140                 return 0;
141         }
142         while (memcmp(t_magic, active_time_magic, 11) != 0)
143         {
144                 if (file_seek(wth->fh, -10, SEEK_CUR, err) == -1)
145                         return -1;
146                 wth->data_offset += 1;
147                 errno = WTAP_ERR_CANT_READ;
148                 bytes_read = file_read(t_magic, 11, wth->fh);
149                 if (bytes_read != 11) {
150                         *err = file_error(wth->fh, err_info);
151                         if (*err != 0)
152                                 return -1;
153                         return 0;
154                 }
155         }
156         if (file_seek(wth->fh, -43, SEEK_CUR, err) == -1) return -1;
157         wth->data_offset -= 32;
158
159         /* Get capture start time */
160         errno = WTAP_ERR_CANT_READ;
161         bytes_read = file_read(&start_date, sizeof(struct frame_date),
162                                wth->fh);
163         if (bytes_read != sizeof(struct frame_date)) {
164                 *err = file_error(wth->fh, err_info);
165                 if (*err != 0)
166                         return -1;
167                 return 0;
168         }
169         wth->data_offset += sizeof(struct frame_date);
170
171         /* This is a radcom file */
172         wth->file_type = WTAP_FILE_RADCOM;
173         wth->subtype_read = radcom_read;
174         wth->subtype_seek_read = radcom_seek_read;
175         wth->snapshot_length = 0; /* not available in header, only in frame */
176         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
177
178 #if 0
179         tm.tm_year = pletohs(&start_date.year)-1900;
180         tm.tm_mon = start_date.month-1;
181         tm.tm_mday = start_date.day;
182         sec = pletohl(&start_date.sec);
183         tm.tm_hour = sec/3600;
184         tm.tm_min = (sec%3600)/60;
185         tm.tm_sec = sec%60;
186         tm.tm_isdst = -1;
187 #endif
188         if (file_seek(wth->fh, sizeof(struct frame_date), SEEK_CUR, err) == -1)
189                 return -1;
190         wth->data_offset += sizeof(struct frame_date);
191
192         errno = WTAP_ERR_CANT_READ;
193         bytes_read = file_read(search_encap, 4, wth->fh);
194         if (bytes_read != 4) {
195                 goto read_error;
196         }
197         wth->data_offset += 4;
198         while (memcmp(encap_magic, search_encap, 4)) {
199                 if (file_seek(wth->fh, -3, SEEK_CUR, err) == -1)
200                         return -1;
201                 wth->data_offset -= 3;
202                 errno = WTAP_ERR_CANT_READ;
203                 bytes_read = file_read(search_encap, 4, wth->fh);
204                 if (bytes_read != 4) {
205                         goto read_error;
206                 }
207                 wth->data_offset += 4;
208         }
209         if (file_seek(wth->fh, 12, SEEK_CUR, err) == -1)
210                 return -1;
211         wth->data_offset += 12;
212         errno = WTAP_ERR_CANT_READ;
213         bytes_read = file_read(search_encap, 4, wth->fh);
214         if (bytes_read != 4) {
215                 goto read_error;
216         }
217         wth->data_offset += 4;
218         if (memcmp(search_encap, "LAPB", 4) == 0)
219                 wth->file_encap = WTAP_ENCAP_LAPB;
220         else if (memcmp(search_encap, "Ethe", 4) == 0)
221                 wth->file_encap = WTAP_ENCAP_ETHERNET;
222         else if (memcmp(search_encap, "ATM/", 4) == 0)
223                 wth->file_encap = WTAP_ENCAP_ATM_RFC1483;
224         else {
225                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
226                 *err_info = g_strdup_printf("radcom: network type \"%.4s\" unknown", search_encap);
227                 return -1;
228         }
229
230 #if 0
231         bytes_read = file_read(&next_date, sizeof(struct frame_date), wth->fh);
232         errno = WTAP_ERR_CANT_READ;
233         if (bytes_read != sizeof(struct frame_date)) {
234                 goto read_error;
235         }
236
237         while (memcmp(&start_date, &next_date, 4)) {
238                 if (file_seek(wth->fh, 1-sizeof(struct frame_date), SEEK_CUR, err) == -1)
239                         return -1;
240                 errno = WTAP_ERR_CANT_READ;
241                 bytes_read = file_read(&next_date, sizeof(struct frame_date),
242                                    wth->fh);
243                 if (bytes_read != sizeof(struct frame_date)) {
244                         goto read_error;
245                 }
246         }
247 #endif
248
249         if (wth->file_encap == WTAP_ENCAP_ETHERNET) {
250                 if (file_seek(wth->fh, 294, SEEK_CUR, err) == -1)
251                         return -1;
252                 wth->data_offset += 294;
253         } else if (wth->file_encap == WTAP_ENCAP_LAPB) {
254                 if (file_seek(wth->fh, 297, SEEK_CUR, err) == -1)
255                         return -1;
256                 wth->data_offset += 297;
257         } else if (wth->file_encap == WTAP_ENCAP_ATM_RFC1483) {
258                 if (file_seek(wth->fh, 504, SEEK_CUR, err) == -1)
259                         return -1;
260                 wth->data_offset += 504;
261         }
262
263         return 1;
264
265 read_error:
266         *err = file_error(wth->fh, err_info);
267         if (*err != 0)
268                 return -1;
269         return 0;
270 }
271
272 /* Read the next packet */
273 static gboolean radcom_read(wtap *wth, int *err, gchar **err_info,
274                             gint64 *data_offset)
275 {
276         int     ret;
277         struct radcomrec_hdr hdr;
278         guint16 data_length, real_length, length;
279         guint32 sec;
280         int     bytes_read;
281         struct tm tm;
282         guint8  phdr[8];
283         char    fcs[2];
284
285         /* Read record header. */
286         *data_offset = wth->data_offset;
287         ret = radcom_read_rec_header(wth->fh, &hdr, err, err_info);
288         if (ret <= 0) {
289                 /* Read error or EOF */
290                 return FALSE;
291         }
292         wth->data_offset += sizeof hdr;
293         data_length = pletohs(&hdr.data_length);
294         if (data_length == 0) {
295                 /*
296                  * The last record appears to have 0 in its "data_length"
297                  * field, but non-zero values in other fields, so we
298                  * check for that and treat it as an EOF indication.
299                  */
300                 *err = 0;
301                 return FALSE;
302         }
303         length = pletohs(&hdr.length);
304         real_length = pletohs(&hdr.real_length);
305
306         if (wth->file_encap == WTAP_ENCAP_LAPB) {
307                 length -= 2; /* FCS */
308                 real_length -= 2;
309         }
310
311         wth->phdr.len = real_length;
312         wth->phdr.caplen = length;
313
314         tm.tm_year = pletohs(&hdr.date.year)-1900;
315         tm.tm_mon = (hdr.date.month&0x0f)-1;
316         tm.tm_mday = hdr.date.day;
317         sec = pletohl(&hdr.date.sec);
318         tm.tm_hour = sec/3600;
319         tm.tm_min = (sec%3600)/60;
320         tm.tm_sec = sec%60;
321         tm.tm_isdst = -1;
322         wth->phdr.ts.secs = mktime(&tm);
323         wth->phdr.ts.nsecs = pletohl(&hdr.date.usec) * 1000;
324
325         switch (wth->file_encap) {
326
327         case WTAP_ENCAP_ETHERNET:
328                 /* XXX - is there an FCS? */
329                 wth->pseudo_header.eth.fcs_len = -1;
330                 break;
331
332         case WTAP_ENCAP_LAPB:
333                 wth->pseudo_header.x25.flags = (hdr.dce & 0x1) ?
334                     0x00 : FROM_DCE;
335                 break;
336
337         case WTAP_ENCAP_ATM_RFC1483:
338                 /*
339                  * XXX - is this stuff a pseudo-header?
340                  * The direction appears to be in the "hdr.dce" field.
341                  */
342                 if (!radcom_read_rec_data(wth->fh, phdr, sizeof phdr, err,
343                     err_info))
344                         return FALSE;   /* Read error */
345                 wth->data_offset += 8;
346                 length -= 8;
347                 wth->phdr.len -= 8;
348                 wth->phdr.caplen -= 8;
349                 break;
350         }
351
352         /*
353          * Read the packet data.
354          */
355         buffer_assure_space(wth->frame_buffer, length);
356         if (!radcom_read_rec_data(wth->fh,
357             buffer_start_ptr(wth->frame_buffer), length, err, err_info))
358                 return FALSE;   /* Read error */
359         wth->data_offset += length;
360
361         if (wth->file_encap == WTAP_ENCAP_LAPB) {
362                 /* Read the FCS.
363                    XXX - should we have some way of indicating the
364                    presence and size of an FCS to our caller?
365                    That'd let us handle other file types as well. */
366                 errno = WTAP_ERR_CANT_READ;
367                 bytes_read = file_read(&fcs, sizeof fcs, wth->fh);
368                 if (bytes_read != sizeof fcs) {
369                         *err = file_error(wth->fh, err_info);
370                         if (*err == 0)
371                                 *err = WTAP_ERR_SHORT_READ;
372                         return FALSE;
373                 }
374                 wth->data_offset += sizeof fcs;
375         }
376
377         return TRUE;
378 }
379
380 static gboolean
381 radcom_seek_read(wtap *wth, gint64 seek_off,
382                  union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
383                  int *err, gchar **err_info)
384 {
385         int     ret;
386         struct radcomrec_hdr hdr;
387         guint8  phdr[8];
388
389         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
390                 return FALSE;
391
392         /* Read record header. */
393         ret = radcom_read_rec_header(wth->random_fh, &hdr, err, err_info);
394         if (ret <= 0) {
395                 /* Read error or EOF */
396                 if (ret == 0) {
397                         /* EOF means "short read" in random-access mode */
398                         *err = WTAP_ERR_SHORT_READ;
399                 }
400                 return FALSE;
401         }
402
403         switch (wth->file_encap) {
404
405         case WTAP_ENCAP_ETHERNET:
406                 /* XXX - is there an FCS? */
407                 pseudo_header->eth.fcs_len = -1;
408                 break;
409
410         case WTAP_ENCAP_LAPB:
411                 pseudo_header->x25.flags = (hdr.dce & 0x1) ? 0x00 : FROM_DCE;
412                 break;
413
414         case WTAP_ENCAP_ATM_RFC1483:
415                 /*
416                  * XXX - is this stuff a pseudo-header?
417                  * The direction appears to be in the "hdr.dce" field.
418                  */
419                 if (!radcom_read_rec_data(wth->random_fh, phdr, sizeof phdr,
420                     err, err_info))
421                         return FALSE;   /* Read error */
422                 break;
423         }
424
425         /*
426          * Read the packet data.
427          */
428         return radcom_read_rec_data(wth->random_fh, pd, length, err, err_info);
429 }
430
431 static int
432 radcom_read_rec_header(FILE_T fh, struct radcomrec_hdr *hdr, int *err,
433                        gchar **err_info)
434 {
435         int     bytes_read;
436
437         errno = WTAP_ERR_CANT_READ;
438         bytes_read = file_read(hdr, sizeof *hdr, fh);
439         if (bytes_read != sizeof *hdr) {
440                 *err = file_error(fh, err_info);
441                 if (*err != 0)
442                         return -1;
443                 if (bytes_read != 0) {
444                         *err = WTAP_ERR_SHORT_READ;
445                         return -1;
446                 }
447                 return 0;
448         }
449         return 1;
450 }
451
452 static gboolean
453 radcom_read_rec_data(FILE_T fh, guint8 *pd, int length, int *err,
454                      gchar **err_info)
455 {
456         int     bytes_read;
457
458         errno = WTAP_ERR_CANT_READ;
459         bytes_read = file_read(pd, length, fh);
460
461         if (bytes_read != length) {
462                 *err = file_error(fh, err_info);
463                 if (*err == 0)
464                         *err = WTAP_ERR_SHORT_READ;
465                 return FALSE;
466         }
467         return TRUE;
468 }