"This file format can't be written to a pipe" and "this file format
[obnox/wireshark/wip.git] / wiretap / eyesdn.c
1 /* eyesdn.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 #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 (usec in network byte order)
45  * - Bytes 3-7: timestamp (40bits sec since 1970 in network byte order)
46  * - Byte 8: channel (0 for D channel, 1-30 for B1-B30)
47  * - Byte 9: Sender Bit 0(0 NT, 1 TE), Protocol in Bits 7:1, see enum
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
81         return i;
82 }
83
84 /* Magic text to check for eyesdn-ness of file */
85 static const unsigned char eyesdn_hdr_magic[]  =
86 { 'E', 'y', 'e', 'S', 'D', 'N'};
87 #define EYESDN_HDR_MAGIC_SIZE  (sizeof(eyesdn_hdr_magic)  / sizeof(eyesdn_hdr_magic[0]))
88
89 /* Size of a record header */
90 #define EYESDN_HDR_LENGTH               12
91
92 /*
93  * XXX - is this the biggest packet we can get?
94  */
95 #define EYESDN_MAX_PACKET_LEN   16384
96
97 static gboolean eyesdn_read(wtap *wth, int *err, gchar **err_info,
98         gint64 *data_offset);
99 static gboolean eyesdn_seek_read(wtap *wth, gint64 seek_off,
100         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
101         int *err, gchar **err_info);
102 static gboolean parse_eyesdn_packet_data(FILE_T fh, int pkt_len, guint8* buf,
103         int *err, gchar **err_info);
104 static int parse_eyesdn_rec_hdr(wtap *wth, FILE_T fh,
105         union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info);
106
107 /* Seeks to the beginning of the next packet, and returns the
108    byte offset.  Returns -1 on failure, and sets "*err" to the error. */
109 static gint64 eyesdn_seek_next_packet(wtap *wth, int *err)
110 {
111         int byte;
112         gint64 cur_off;
113
114         while ((byte = file_getc(wth->fh)) != EOF) {
115                 if (byte == 0xff) {
116                         cur_off = file_tell(wth->fh);
117                         if (cur_off == -1) {
118                                 /* Error. */
119                                 *err = file_error(wth->fh);
120                                 return -1;
121                         }
122                         return cur_off;
123                 }
124         }
125         if (file_eof(wth->fh)) {
126                 /* We got an EOF. */
127                 *err = 0;
128         } else {
129                 /* We got an error. */
130                 *err = file_error(wth->fh);
131         }
132         return -1;
133 }
134
135 int eyesdn_open(wtap *wth, int *err, gchar **err_info _U_)
136 {
137         int     bytes_read;
138         char    magic[EYESDN_HDR_MAGIC_SIZE];
139
140         /* Look for eyesdn header */
141         errno = WTAP_ERR_CANT_READ;
142         bytes_read = file_read(&magic, sizeof magic, wth->fh);
143         if (bytes_read != sizeof magic) {
144                 *err = file_error(wth->fh);
145                 if (*err != 0)
146                         return -1;
147                 return 0;
148         }
149         if (memcmp(magic, eyesdn_hdr_magic, EYESDN_HDR_MAGIC_SIZE) != 0)
150                 return 0;
151
152         wth->data_offset = 0;
153         wth->file_encap = WTAP_ENCAP_PER_PACKET;
154         wth->file_type = WTAP_FILE_EYESDN;
155         wth->snapshot_length = 0; /* not known */
156         wth->subtype_read = eyesdn_read;
157         wth->subtype_seek_read = eyesdn_seek_read;
158         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
159
160         return 1;
161 }
162
163 /* Find the next packet and parse it; called from wtap_read(). */
164 static gboolean eyesdn_read(wtap *wth, int *err, gchar **err_info,
165     gint64 *data_offset)
166 {
167         gint64  offset;
168         guint8  *buf;
169         int     pkt_len;
170
171         /* Find the next packet */
172         offset = eyesdn_seek_next_packet(wth, err);
173         if (offset < 1)
174                 return FALSE;
175
176         /* Parse the header */
177         pkt_len = parse_eyesdn_rec_hdr(wth, wth->fh, &wth->pseudo_header, err,
178             err_info);
179         if (pkt_len == -1)
180                 return FALSE;
181
182         /* Make sure we have enough room for the packet */
183         buffer_assure_space(wth->frame_buffer, EYESDN_MAX_PACKET_LEN);
184         buf = buffer_start_ptr(wth->frame_buffer);
185
186         /* Read the packet data */
187         if (!parse_eyesdn_packet_data(wth->fh, pkt_len, buf, err, err_info))
188                 return FALSE;
189
190         wth->data_offset = offset;
191         *data_offset = offset;
192         return TRUE;
193 }
194
195 /* Used to read packets in random-access fashion */
196 static gboolean
197 eyesdn_seek_read (wtap *wth, gint64 seek_off,
198         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
199         int *err, gchar **err_info)
200 {
201         int     pkt_len;
202
203         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
204                 return FALSE;
205
206         pkt_len = parse_eyesdn_rec_hdr(NULL, wth->random_fh, pseudo_header,
207             err, err_info);
208
209         if (pkt_len != len) {
210                 if (pkt_len != -1) {
211                         *err = WTAP_ERR_BAD_RECORD;
212                         *err_info = g_strdup_printf("eyesdn: requested length %d doesn't match length %d",
213                             len, pkt_len);
214                 }
215                 return FALSE;
216         }
217
218         return parse_eyesdn_packet_data(wth->random_fh, pkt_len, pd, err,
219             err_info);
220 }
221
222 /* Parses a packet record header. */
223 static int
224 parse_eyesdn_rec_hdr(wtap *wth, FILE_T fh,
225     union wtap_pseudo_header *pseudo_header, int *err, gchar **err_info)
226 {
227         guint8          hdr[EYESDN_HDR_LENGTH];
228         time_t          secs;
229         int             usecs;
230         int             pkt_len;
231         guint8          channel, direction;
232
233         /* Our file pointer should be at the summary information header
234          * for a packet. Read in that header and extract the useful
235          * information.
236          */
237         if (esc_read(hdr, EYESDN_HDR_LENGTH, fh) != EYESDN_HDR_LENGTH) {
238                 *err = file_error(fh);
239                 if (*err == 0)
240                         *err = WTAP_ERR_SHORT_READ;
241                 return -1;
242         }
243     
244         /* extract information from header */
245         usecs = pntoh24(&hdr[0]);
246 #ifdef TV64BITS    
247         secs = hdr[3];
248 #else    
249         secs = 0;
250 #endif    
251         secs = (secs << 8) | hdr[4];
252         secs = (secs << 8) | hdr[5];
253         secs = (secs << 8) | hdr[6];
254         secs = (secs << 8) | hdr[7];
255
256         channel = hdr[8];
257         direction = hdr[9];
258         pkt_len = pntohs(&hdr[10]);
259
260         switch(direction >> 1) {
261
262         default:
263         case EYESDN_ENCAP_ISDN: /* ISDN */
264                 pseudo_header->isdn.uton = direction & 1;
265                 pseudo_header->isdn.channel = channel;
266                 if(channel) { /* bearer channels */
267                         if(wth) {
268                                 wth->phdr.pkt_encap = WTAP_ENCAP_ISDN; /* recognises PPP */
269                                 pseudo_header->isdn.uton=!pseudo_header->isdn.uton; /* bug */
270                         }
271                 } else { /* D channel */
272                         if(wth) {
273                                 wth->phdr.pkt_encap = WTAP_ENCAP_ISDN;
274                         }
275                 }
276                 break;
277
278         case EYESDN_ENCAP_MSG: /* Layer 1 message */
279                 if(wth) {
280                         wth->phdr.pkt_encap = WTAP_ENCAP_LAYER1_EVENT;
281                 }
282                 pseudo_header->l1event.uton = (direction & 1);
283                 break;
284
285         case EYESDN_ENCAP_LAPB: /* X.25 via LAPB */ 
286                 if(wth) {
287                         wth->phdr.pkt_encap = WTAP_ENCAP_LAPB;
288                 }
289                 pseudo_header->x25.flags = (direction & 1) ? 0 : 0x80;
290                 break;
291
292         case EYESDN_ENCAP_ATM: { /* ATM cells */
293 #define CELL_LEN 53
294                 unsigned char cell[CELL_LEN];
295                 gint64 cur_off;
296
297                 if(pkt_len != CELL_LEN) {
298                         *err = WTAP_ERR_BAD_RECORD;
299                         *err_info = g_strdup_printf(
300                             "eyesdn: ATM cell has a length != 53 (%u)",
301                             pkt_len);
302                         return -1;
303                 }
304
305                 cur_off = file_tell(fh);
306                 if (esc_read(cell, CELL_LEN, fh) != CELL_LEN) {
307                         *err = file_error(fh);
308                         if (*err == 0)
309                                 *err = WTAP_ERR_SHORT_READ;
310                         return -1;
311                 }
312                 if (file_seek(fh, cur_off, SEEK_SET, err) == -1)
313                         return -1;
314                 if(wth) {
315                         wth->phdr.pkt_encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
316                 }
317                 pseudo_header->atm.flags=ATM_RAW_CELL;
318                 pseudo_header->atm.aal=AAL_UNKNOWN;
319                 pseudo_header->atm.type=TRAF_UMTS_FP;
320                 pseudo_header->atm.subtype=TRAF_ST_UNKNOWN;
321                 pseudo_header->atm.vpi=((cell[0]&0xf)<<4) + (cell[0]&0xf);
322                 pseudo_header->atm.vci=((cell[0]&0xf)<<4) + cell[0]; /* from cell */
323                 pseudo_header->atm.channel=direction & 1;
324                 }
325                 break;
326
327         case EYESDN_ENCAP_MTP2: /* SS7 frames */
328                 pseudo_header->mtp2.sent = direction & 1;
329                 pseudo_header->mtp2.annex_a_used = MTP2_ANNEX_A_USED_UNKNOWN;
330                 pseudo_header->mtp2.link_number = channel;              
331                 if(wth) {
332                         wth->phdr.pkt_encap = WTAP_ENCAP_MTP2;
333                 }
334                 break;
335
336         case EYESDN_ENCAP_DPNSS: /* DPNSS */
337                 pseudo_header->isdn.uton = direction & 1;
338                 pseudo_header->isdn.channel = channel;
339                 if(wth) {
340                         wth->phdr.pkt_encap = WTAP_ENCAP_DPNSS;
341                 }
342                 break;
343
344         case EYESDN_ENCAP_DASS2: /* DASS2 frames */
345                 pseudo_header->isdn.uton = direction & 1;
346                 pseudo_header->isdn.channel = channel;
347                 if(wth) {
348                         wth->phdr.pkt_encap = WTAP_ENCAP_DPNSS;
349                 }
350                 break;
351
352         case EYESDN_ENCAP_BACNET: /* BACNET async over HDLC frames */
353                 /* pseudo_header->isdn.uton = direction & 1; */
354                 /* pseudo_header->isdn.channel = channel; */
355                 if(wth) {
356                         wth->phdr.pkt_encap = WTAP_ENCAP_BACNET_MS_TP;
357                 }
358                 break;
359         }
360
361         if(pkt_len > EYESDN_MAX_PACKET_LEN) {
362                 *err = WTAP_ERR_BAD_RECORD;
363                 *err_info = g_strdup_printf("eyesdn: File has %u-byte packet, bigger than maximum of %u",
364                 pkt_len, EYESDN_MAX_PACKET_LEN);
365                 return -1;
366         }
367
368         if (wth) {
369                 wth->phdr.ts.secs = secs;
370                 wth->phdr.ts.nsecs = usecs * 1000;
371                 wth->phdr.caplen = pkt_len;
372                 wth->phdr.len = pkt_len;
373         }
374
375         return pkt_len;
376 }
377
378 /* read a packet */
379 static gboolean
380 parse_eyesdn_packet_data(FILE_T fh, int pkt_len, guint8* buf, int *err,
381     gchar **err_info)
382 {
383         int bytes_read;
384
385         errno = WTAP_ERR_CANT_READ;
386         bytes_read = esc_read(buf, pkt_len, fh);
387         if (bytes_read != pkt_len) {
388                 if (bytes_read == -2) {
389                         *err = file_error(fh);
390                         if (*err == 0)
391                                 *err = WTAP_ERR_SHORT_READ;
392                 } else if (bytes_read == -1) {
393                         *err = WTAP_ERR_BAD_RECORD;
394                         *err_info = g_strdup("eyesdn: No flag character seen in frame");
395                 } else
396                         *err = WTAP_ERR_SHORT_READ;
397                 return FALSE;
398         }
399         return TRUE;
400 }
401
402
403 static gboolean
404 esc_write(wtap_dumper *wdh, const guint8 *buf, int len, int *err)
405 {
406         int i;
407         guint8 byte;
408         static const guint8 esc = 0xfe;
409         
410         for(i=0; i<len; i++) {
411                 byte=buf[i];
412                 if(byte == 0xff || byte == 0xfe) {
413                         /*
414                          * Escape the frame delimiter and escape byte.
415                          */
416                         if (!wtap_dump_file_write(wdh, &esc, sizeof esc, err))
417                                 return FALSE;
418                         byte-=2;
419                 }
420                 if (!wtap_dump_file_write(wdh, &byte, sizeof byte, err))
421                         return FALSE;
422         }
423         return TRUE;
424 }
425
426 static gboolean eyesdn_dump(wtap_dumper *wdh,
427                             const struct wtap_pkthdr *phdr,
428                             const union wtap_pseudo_header *pseudo_header _U_,
429                             const guchar *pd, int *err);
430
431 gboolean eyesdn_dump_open(wtap_dumper *wdh, int *err)
432 {
433         wdh->subtype_write=eyesdn_dump;
434         wdh->subtype_close=NULL;
435
436         if (!wtap_dump_file_write(wdh, eyesdn_hdr_magic,
437             EYESDN_HDR_MAGIC_SIZE, err))
438                 return FALSE;
439         wdh->bytes_dumped += EYESDN_HDR_MAGIC_SIZE;
440         *err=0;
441         return TRUE;
442 }
443
444 int eyesdn_dump_can_write_encap(int encap)
445 {
446         switch (encap) {
447         case WTAP_ENCAP_ISDN:
448         case WTAP_ENCAP_LAYER1_EVENT:
449         case WTAP_ENCAP_DPNSS:
450         case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
451         case WTAP_ENCAP_LAPB:
452         case WTAP_ENCAP_MTP2:
453         case WTAP_ENCAP_BACNET_MS_TP:
454         case WTAP_ENCAP_PER_PACKET:
455                 return 0;
456
457         default:
458                 return WTAP_ERR_UNSUPPORTED_ENCAP;
459         }
460 }
461
462 /* Write a record for a packet to a dump file.
463  *    Returns TRUE on success, FALSE on failure. */
464 static gboolean eyesdn_dump(wtap_dumper *wdh,
465                             const struct wtap_pkthdr *phdr,
466                             const union wtap_pseudo_header *pseudo_header _U_,
467                             const guchar *pd, int *err)
468 {
469         static const guint8 start_flag = 0xff;
470         guint8 buf[EYESDN_HDR_LENGTH];
471         int usecs;
472         time_t secs;
473         int channel;
474         int origin;
475         int protocol;
476         int size;
477
478         usecs=phdr->ts.nsecs/1000;
479         secs=phdr->ts.secs;
480         size=phdr->caplen;
481         origin = pseudo_header->isdn.uton;
482         channel = pseudo_header->isdn.channel;
483
484         switch(phdr->pkt_encap) {
485
486         case WTAP_ENCAP_ISDN:
487                 protocol=EYESDN_ENCAP_ISDN; /* set depending on decoder format and mode */
488                 break;
489
490         case WTAP_ENCAP_LAYER1_EVENT:
491                 protocol=EYESDN_ENCAP_MSG;
492                 break;
493
494         case WTAP_ENCAP_DPNSS:
495                 protocol=EYESDN_ENCAP_DPNSS;
496                 break;
497
498 #if 0
499         case WTAP_ENCAP_DASS2:
500                 protocol=EYESDN_ENCAP_DASS2;
501                 break;
502 #endif
503
504         case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
505                 protocol=EYESDN_ENCAP_ATM;
506                 channel=0x80;
507                 break;
508
509         case WTAP_ENCAP_LAPB:
510                 protocol=EYESDN_ENCAP_LAPB;
511                 break;
512
513         case WTAP_ENCAP_MTP2:
514                 protocol=EYESDN_ENCAP_MTP2;
515                 break;
516
517         case WTAP_ENCAP_BACNET_MS_TP:
518                 protocol=EYESDN_ENCAP_BACNET;
519                 break;
520
521         default:
522                 *err=WTAP_ERR_UNSUPPORTED_ENCAP;
523                 return FALSE;
524         }
525
526         phton24(&buf[0], usecs);
527
528         buf[3] = (guint8)0;
529         buf[4] = (guint8)(0xff & (secs >> 24));
530         buf[5] = (guint8)(0xff & (secs >> 16));
531         buf[6] = (guint8)(0xff & (secs >> 8));
532         buf[7] = (guint8)(0xff & (secs >> 0));
533
534         buf[8] = (guint8) channel;
535         buf[9] = (guint8) (origin?1:0) + (protocol << 1);
536         phtons(&buf[10], size);
537         
538         /* start flag */
539         if (!wtap_dump_file_write(wdh, &start_flag, sizeof start_flag, err))
540                 return FALSE;
541         if (!esc_write(wdh, buf, 12, err))
542                 return FALSE;
543         if (!esc_write(wdh, pd, size, err))
544                 return FALSE;
545         return TRUE;
546 }