Use "guint8" for 8-bit binary data.
[obnox/wireshark/wip.git] / wiretap / eyesdn.c
1 /* eyesdn.c
2  *
3  * $Id: eyesdn.c,v 1.2 2004/02/12 21:25:07 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 #include "wtap-int.h"
27 #include "buffer.h"
28 #include "eyesdn.h"
29 #include "file_wrappers.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35
36 /* This module reads the output of the EyeSDN USB S0/E1 ISDN probes
37  * They store HDLC frames of D and B channels in a binary format
38  * The fileformat is
39  * 
40  * 1-6 Byte: EyeSDN - Magic
41  * 7-n Byte: Frames
42  * 
43  * Each Frame starts with the 0xff Flag byte
44  * - Bytes 0-2: timestamp (long usec in network byte order)
45  * - Bytes 3-7: timestamp (40bits sec since 1970 in network byte order)
46  * - Byte 8: channel (0d for D channel, 0e for e channel, b1-ce for B1-B30)
47  * - Byte 9: Sender (0 NT, 1 TE)
48  * - Byte 10-11: frame size in bytes
49  * - Byte 12-n: Frame Payload
50  * 
51  * All multibyte values are represented in network byte order
52  * The frame is terminated with a flag character (0xff)
53  * bytes 0xff within a frame are escaped using the 0xfe escape character
54  * the byte following the escape character is decremented by two:
55  * so 0xfe 0xfd is actually a 0xff
56  * Characters that need to be escaped are 0xff and 0xfe
57  */
58
59
60 static int esc_read(guint8 *buf, int len, FILE_T *fh)
61 {
62     int i;
63     int value;
64     
65     for(i=0; i<len; i++) {
66         value=file_getc(fh);
67         if(value==-1)
68             return -2; /* EOF or error */
69         if(value==0xff)
70             return -1; /* error !!, read into next frame */
71         if(value==0xfe) {
72             /* we need to escape */
73             value=file_getc(fh);
74             if(value==-1)
75                 return -2;
76             value+=2;
77         }
78         buf[i]=value;
79     }
80     return i;
81 }
82
83 /* Magic text to check for eyesdn-ness of file */
84 static const unsigned char eyesdn_hdr_magic[]  =
85 { 'E', 'y', 'e', 'S', 'D', 'N'};
86 #define EYESDN_HDR_MAGIC_SIZE  (sizeof(eyesdn_hdr_magic)  / sizeof(eyesdn_hdr_magic[0]))
87
88 /* Magic text for start of packet */
89 static const unsigned char eyesdn_rec_magic[]  = { 0xff };
90 #define EYESDN_REC_MAGIC_SIZE  (sizeof eyesdn_rec_magic  / sizeof eyesdn_rec_magic[0])
91
92 #define EYESDN_HEADER_LINES_TO_CHECK    1
93 #define EYESDN_HDR_LENGTH               12
94 #define EYESDN_LINE_LENGTH              12
95
96 /*
97  * XXX - is this the biggest packet we can get?
98  */
99 #define EYESDN_MAX_PACKET_LEN   16384
100
101 static gboolean eyesdn_read(wtap *wth, int *err, gchar **err_info,
102         long *data_offset);
103 static gboolean eyesdn_seek_read(wtap *wth, long seek_off,
104         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
105         int *err, gchar **err_info);
106 static gboolean parse_eyesdn_packet_data(FILE_T fh, int pkt_len, guint8* buf,
107         int *err, gchar **err_info);
108 static int parse_eyesdn_rec_hdr(wtap *wth, FILE_T fh,
109         union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
110
111 /* Seeks to the beginning of the next packet, and returns the
112    byte offset.  Returns -1 on failure, and sets "*err" to the error. */
113 static long eyesdn_seek_next_packet(wtap *wth, int *err)
114 {
115   int byte;
116   long cur_off;
117
118   while ((byte = file_getc(wth->fh)) != EOF) {
119     if (byte == 0xff) {
120         cur_off = file_tell(wth->fh);
121         if (cur_off == -1) {
122           /* Error. */
123           *err = file_error(wth->fh);
124           return -1;
125         }
126         return cur_off;
127       }
128   }
129   if (file_eof(wth->fh)) {
130     /* We got an EOF. */
131     *err = 0;
132   } else {
133     /* We (presumably) got an error (there's no equivalent to "ferror()"
134        in zlib, alas, so we don't have a wrapper to check for an error). */
135     *err = file_error(wth->fh);
136   }
137   return -1;
138 }
139
140 /* Look through the first part of a file to see if this is
141  * a eyesdn trace file.
142  *
143  * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
144  * if we get an I/O error, "*err" will be set to a non-zero value.
145  */
146 static gboolean eyesdn_check_file_type(wtap *wth, int *err)
147 {
148         char    buf[EYESDN_HDR_MAGIC_SIZE];
149         int     i, reclen;
150         guint   level;
151         char    byte;
152
153         reclen=EYESDN_HDR_MAGIC_SIZE;
154         if (file_read(buf, 1, reclen, wth->fh) == reclen) {
155             level = 0;
156             for (i = 0; i < reclen; i++) {
157                 byte = buf[i];
158                 if (byte == eyesdn_hdr_magic[level]) {
159                     level++;
160                     if (level >= EYESDN_HDR_MAGIC_SIZE) {
161                         return TRUE;
162                     }
163                 } else {
164                     level = 0;
165                 }
166             }
167         } else {/* EOF or error. */
168             if (file_eof(wth->fh))
169                 *err = 0;
170             else
171                 *err = file_error(wth->fh);
172             return FALSE;
173         }
174         *err = 0;
175         return FALSE;
176 }
177
178
179 int eyesdn_open(wtap *wth, int *err, gchar **err_info _U_)
180 {
181         /* Look for eyesdn header */
182         if (!eyesdn_check_file_type(wth, err)) {
183                 if (*err == 0)
184                         return 0;
185                 else
186                         return -1;
187         }
188
189         wth->data_offset = 0;
190         wth->file_encap = WTAP_ENCAP_ISDN;
191         wth->file_type = WTAP_FILE_EYESDN;
192         wth->snapshot_length = 0; /* not known */
193         wth->subtype_read = eyesdn_read;
194         wth->subtype_seek_read = eyesdn_seek_read;
195
196         return 1;
197 }
198
199 /* Find the next packet and parse it; called from wtap_loop() and wtap_read(). */
200 static gboolean eyesdn_read(wtap *wth, int *err, gchar **err_info,
201     long *data_offset)
202 {
203         long    offset;
204         guint8  *buf;
205         int     pkt_len;
206
207         /* Find the next packet */
208         offset = eyesdn_seek_next_packet(wth, err);
209         if (offset < 1)
210                 return FALSE;
211
212         /* Parse the header */
213         pkt_len = parse_eyesdn_rec_hdr(wth, wth->fh, &wth->pseudo_header, err,
214             err_info);
215         if (pkt_len == -1)
216                 return FALSE;
217
218         /* Make sure we have enough room for the packet */
219         buffer_assure_space(wth->frame_buffer, EYESDN_MAX_PACKET_LEN);
220         buf = buffer_start_ptr(wth->frame_buffer);
221
222         /* Read the packet data */
223         if (!parse_eyesdn_packet_data(wth->fh, pkt_len, buf, err, err_info))
224                 return FALSE;
225
226         wth->data_offset = offset;
227         *data_offset = offset;
228         return TRUE;
229 }
230
231 /* Used to read packets in random-access fashion */
232 static gboolean
233 eyesdn_seek_read (wtap *wth, long seek_off,
234         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
235         int *err, gchar **err_info)
236 {
237         int     pkt_len;
238
239         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
240                 return FALSE;
241
242         pkt_len = parse_eyesdn_rec_hdr(NULL, wth->random_fh, pseudo_header,
243             err, err_info);
244
245         if (pkt_len != len) {
246                 if (pkt_len != -1) {
247                         *err = WTAP_ERR_BAD_RECORD;
248                         *err_info = g_strdup_printf("eyesdn: requested length %d doesn't match length %d",
249                             len, pkt_len);
250                 }
251                 return FALSE;
252         }
253
254         return parse_eyesdn_packet_data(wth->random_fh, pkt_len, pd, err,
255             err_info);
256 }
257
258 /* Parses a packet record header. */
259 static int
260 parse_eyesdn_rec_hdr(wtap *wth, FILE_T fh,
261     union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info)
262 {
263         guint8          hdr[EYESDN_HDR_LENGTH];
264         unsigned long   secs, usecs;
265         int             pkt_len;
266         unsigned int    channel, direction;
267
268         /* Our file pointer should be at the summary information header
269          * for a packet. Read in that header and extract the useful
270          * information.
271          */
272         if (esc_read(hdr, EYESDN_HDR_LENGTH, fh) != EYESDN_HDR_LENGTH) {
273                 *err = file_error(fh);
274                 if (*err == 0)
275                         *err = WTAP_ERR_SHORT_READ;
276                 return -1;
277         }
278     
279         /* extract information from header */
280         usecs = ((unsigned long) hdr[0]);
281         usecs = (usecs << 8) | ((unsigned long) hdr[1]);
282         usecs = (usecs << 8) | ((unsigned long) hdr[2]);
283 #ifdef TV64BITS    
284         secs = ((unsigned long) hdr[3]);
285 #else    
286         secs = 0;
287 #endif    
288         secs = (secs << 8) | ((unsigned long) hdr[4]);
289         secs = (secs << 8) | ((unsigned long) hdr[5]);
290         secs = (secs << 8) | ((unsigned long) hdr[6]);
291         secs = (secs << 8) | ((unsigned long) hdr[7]);
292
293         channel = hdr[8];
294         direction = hdr[9];
295         pkt_len = ((unsigned long) hdr[10]) << 8;
296         pkt_len = (pkt_len << 8) | ((unsigned long) hdr[11]);
297
298         /* sanity checks */
299         if(channel>30) {
300             *err = WTAP_ERR_BAD_RECORD;
301             *err_info = g_strdup_printf("eyesdn: bad channel number %u",
302                 channel);
303             return -1;
304         }
305
306         if(direction>1) {
307             *err = WTAP_ERR_BAD_RECORD;
308             *err_info = g_strdup_printf("eyesdn: bad direction value %u",
309                 direction);
310             return -1;
311         }
312         if(pkt_len > EYESDN_MAX_PACKET_LEN) {
313             *err = WTAP_ERR_BAD_RECORD;
314             *err_info = g_strdup_printf("eyesdn: File has %u-byte packet, bigger than maximum of %u",
315                 pkt_len, EYESDN_MAX_PACKET_LEN);
316             return -1;
317         }
318
319         if (wth) {
320                 wth->phdr.ts.tv_sec = secs;
321                 wth->phdr.ts.tv_usec = usecs;
322                 wth->phdr.caplen = pkt_len;
323                 wth->phdr.len = pkt_len;
324         }
325         pseudo_header->isdn.uton = direction;
326         pseudo_header->isdn.channel = channel;
327
328         return pkt_len;
329 }
330
331 /* read a packet */
332 static gboolean
333 parse_eyesdn_packet_data(FILE_T fh, int pkt_len, guint8* buf, int *err,
334     gchar **err_info)
335 {
336         int bytes_read;
337
338         errno = WTAP_ERR_CANT_READ;
339         bytes_read = esc_read(buf, pkt_len, fh);
340         if (bytes_read != pkt_len) {
341             if (bytes_read == -2) {
342                 *err = file_error(fh);
343                 if (*err == 0)
344                     *err = WTAP_ERR_SHORT_READ;
345             }  else if (bytes_read == -1) {
346                 *err = WTAP_ERR_BAD_RECORD;
347                 *err_info = g_strdup("eyesdn: No flag character seen in frame");
348             } else
349                 *err = WTAP_ERR_SHORT_READ;
350             return FALSE;
351         }
352         return TRUE;
353 }