timestamp display precision:
[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 <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         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
176
177         tm.tm_year = pletohs(&start_date.year)-1900;
178         tm.tm_mon = start_date.month-1;
179         tm.tm_mday = start_date.day;
180         sec = pletohl(&start_date.sec);
181         tm.tm_hour = sec/3600;
182         tm.tm_min = (sec%3600)/60;
183         tm.tm_sec = sec%60;
184         tm.tm_isdst = -1;
185
186         if (file_seek(wth->fh, sizeof(struct frame_date), SEEK_CUR, err) == -1)
187                 return -1;
188         wth->data_offset += sizeof(struct frame_date);
189
190         errno = WTAP_ERR_CANT_READ;
191         bytes_read = file_read(search_encap, 1, 4, wth->fh);
192         if (bytes_read != 4) {
193                 goto read_error;
194         }
195         wth->data_offset += 4;
196         while (memcmp(encap_magic, search_encap, 4)) {
197                 if (file_seek(wth->fh, -3, SEEK_CUR, err) == -1)
198                         return -1;
199                 wth->data_offset -= 3;
200                 errno = WTAP_ERR_CANT_READ;
201                 bytes_read = file_read(search_encap, 1, 4, wth->fh);
202                 if (bytes_read != 4) {
203                         goto read_error;
204                 }
205                 wth->data_offset += 4;
206         }
207         if (file_seek(wth->fh, 12, SEEK_CUR, err) == -1)
208                 return -1;
209         wth->data_offset += 12;
210         errno = WTAP_ERR_CANT_READ;
211         bytes_read = file_read(search_encap, 1, 4, wth->fh);
212         if (bytes_read != 4) {
213                 goto read_error;
214         }
215         wth->data_offset += 4;
216         if (memcmp(search_encap, "LAPB", 4) == 0)
217                 wth->file_encap = WTAP_ENCAP_LAPB;
218         else if (memcmp(search_encap, "Ethe", 4) == 0)
219                 wth->file_encap = WTAP_ENCAP_ETHERNET;
220         else if (memcmp(search_encap, "ATM/", 4) == 0)
221                 wth->file_encap = WTAP_ENCAP_ATM_RFC1483;
222         else {
223                 *err = WTAP_ERR_UNSUPPORTED_ENCAP;
224                 *err_info = g_strdup_printf("radcom: network type \"%.4s\" unknown", search_encap);
225                 return -1;
226         }
227
228         /*bytes_read = file_read(&next_date, 1, sizeof(struct frame_date), wth->fh);
229         errno = WTAP_ERR_CANT_READ;
230         if (bytes_read != sizeof(struct frame_date)) {
231                 goto read_error;
232         }
233
234         while (memcmp(&start_date, &next_date, 4)) {
235                 if (file_seek(wth->fh, 1-sizeof(struct frame_date), SEEK_CUR, err) == -1)
236                         return -1;
237                 errno = WTAP_ERR_CANT_READ;
238                 bytes_read = file_read(&next_date, 1, sizeof(struct frame_date),
239                                    wth->fh);
240                 if (bytes_read != sizeof(struct frame_date)) {
241                         goto read_error;
242                 }
243         }*/
244
245         if (wth->file_encap == WTAP_ENCAP_ETHERNET) {
246                 if (file_seek(wth->fh, 294, SEEK_CUR, err) == -1)
247                         return -1;
248                 wth->data_offset += 294;
249         } else if (wth->file_encap == WTAP_ENCAP_LAPB) {
250                 if (file_seek(wth->fh, 297, SEEK_CUR, err) == -1)
251                         return -1;
252                 wth->data_offset += 297;
253         } else if (wth->file_encap == WTAP_ENCAP_ATM_RFC1483) {
254                 if (file_seek(wth->fh, 504, SEEK_CUR, err) == -1)
255                         return -1;
256                 wth->data_offset += 504;
257         }
258
259         return 1;
260
261 read_error:
262         *err = file_error(wth->fh);
263         if (*err != 0)
264                 return -1;
265         return 0;
266 }
267
268 /* Read the next packet */
269 static gboolean radcom_read(wtap *wth, int *err, gchar **err_info _U_,
270     long *data_offset)
271 {
272         int     ret;
273         struct radcomrec_hdr hdr;
274         guint16 data_length, real_length, length;
275         guint32 sec;
276         int     bytes_read;
277         struct tm tm;
278         guchar  phdr[8];
279         char    fcs[2];
280
281         /* Read record header. */
282         *data_offset = wth->data_offset;
283         ret = radcom_read_rec_header(wth->fh, &hdr, err);
284         if (ret <= 0) {
285                 /* Read error or EOF */
286                 return FALSE;
287         }
288         wth->data_offset += sizeof hdr;
289         data_length = pletohs(&hdr.data_length);
290         if (data_length == 0) {
291                 /*
292                  * The last record appears to have 0 in its "data_length"
293                  * field, but non-zero values in other fields, so we
294                  * check for that and treat it as an EOF indication.
295                  */
296                 *err = 0;
297                 return FALSE;
298         }
299         length = pletohs(&hdr.length);
300         real_length = pletohs(&hdr.real_length);
301
302         if (wth->file_encap == WTAP_ENCAP_LAPB) {
303                 length -= 2; /* FCS */
304                 real_length -= 2;
305         }
306
307         wth->phdr.len = real_length;
308         wth->phdr.caplen = length;
309
310         tm.tm_year = pletohs(&hdr.date.year)-1900;
311         tm.tm_mon = (hdr.date.month&0x0f)-1;
312         tm.tm_mday = hdr.date.day;
313         sec = pletohl(&hdr.date.sec);
314         tm.tm_hour = sec/3600;
315         tm.tm_min = (sec%3600)/60;
316         tm.tm_sec = sec%60;
317         tm.tm_isdst = -1;
318         wth->phdr.ts.secs = mktime(&tm);
319         wth->phdr.ts.nsecs = pletohl(&hdr.date.usec) * 1000;
320
321         switch (wth->file_encap) {
322
323         case WTAP_ENCAP_ETHERNET:
324                 /* XXX - is there an FCS? */
325                 wth->pseudo_header.eth.fcs_len = -1;
326                 break;
327
328         case WTAP_ENCAP_LAPB:
329                 wth->pseudo_header.x25.flags = (hdr.dce & 0x1) ?
330                     0x00 : FROM_DCE;
331                 break;
332
333         case WTAP_ENCAP_ATM_RFC1483:
334                 /*
335                  * XXX - is this stuff a pseudo-header?
336                  * The direction appears to be in the "hdr.dce" field.
337                  */
338                 if (!radcom_read_rec_data(wth->fh, phdr, sizeof phdr, err))
339                         return FALSE;   /* Read error */
340                 wth->data_offset += 8;
341                 length -= 8;
342                 wth->phdr.len -= 8;
343                 wth->phdr.caplen -= 8;
344                 break;
345         }
346
347         /*
348          * Read the packet data.
349          */
350         buffer_assure_space(wth->frame_buffer, length);
351         if (!radcom_read_rec_data(wth->fh,
352             buffer_start_ptr(wth->frame_buffer), length, err))
353                 return FALSE;   /* Read error */
354         wth->data_offset += length;
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 }