4fc9b3b3d28e4a9cdac705be73560d0186ba4a35
[metze/wireshark/wip.git] / wiretap / camins.c
1 /* camins.c
2  *
3  * File format support for Rabbit Labs CAM Inspector files
4  * Copyright (c) 2013 by Martin Kaiser <martin@kaiser.cx>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, write to the Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26
27
28 /* CAM Inspector is a commercial log tool for DVB-CI
29    it stores recorded packets between a CI module and a DVB receiver,
30    using a proprietary file format
31
32    a CAM Inspector file consists of 16bit blocks
33    the first byte contains payload data,
34    the second byte contains a "transaction type"
35
36    we currently support the following transaction types
37
38    0x20 == data transfer from CI module to host
39    0x22 == host reads the lower byte of the size register
40    0x23 == host reads the higher byte of the size register
41    0x2A == host writes the lower byte of the size register
42    0x2B == host writes the higher byte of the size register
43    0x28 == data transfer from host to CI module
44
45    using these transaction types, we can identify and assemble data transfers
46    from the host to the CAM and vice versa
47
48    a host->module data transfer will use the following transactions
49       one 0x2A and one 0x2B transaction to write the 16bit size
50       <size> 0x28 transactions to transfer one byte at a time
51    this will be assembled into one packet
52
53    the module->host transfer is similar
54
55    error handling
56    when we run into an error while assembling a data transfer, the
57    primary goal is to recover so that we can handle the next transfer
58    correctly (all files I used for testing contained errors where
59    apparently the logging hardware missed some bytes)
60 */
61
62 #include "config.h"
63
64 #include <string.h>
65 #include <glib.h>
66 #include <wtap.h>
67 #include <wtap-int.h>
68 #include <file_wrappers.h>
69 #include <buffer.h>
70
71 #include "camins.h"
72
73
74 #define TRANS_CAM_HOST        0x20
75 #define TRANS_READ_SIZE_LOW   0x22
76 #define TRANS_READ_SIZE_HIGH  0x23
77 #define TRANS_HOST_CAM        0x28
78 #define TRANS_WRITE_SIZE_LOW  0x2A
79 #define TRANS_WRITE_SIZE_HIGH 0x2B
80
81 #define IS_TRANS_SIZE(x) \
82     ((x)==TRANS_WRITE_SIZE_LOW || (x)==TRANS_WRITE_SIZE_HIGH || \
83      (x)==TRANS_READ_SIZE_LOW || (x)==TRANS_READ_SIZE_HIGH)
84
85 typedef enum {
86     SIZE_HAVE_NONE,
87     SIZE_HAVE_LOW,
88     SIZE_HAVE_HIGH,
89     SIZE_HAVE_ALL 
90 } size_read_t;
91
92 #define RESET_STAT_VALS \
93 { \
94     *dat_trans_type = 0x00; \
95     *dat_len = 0x00; \
96     size_stat = SIZE_HAVE_NONE; \
97 }
98
99 #define SIZE_ADD_LOW \
100 { size_stat = (size_stat==SIZE_HAVE_HIGH ? SIZE_HAVE_ALL : SIZE_HAVE_LOW); }
101
102 #define SIZE_ADD_HIGH \
103 { size_stat = (size_stat==SIZE_HAVE_LOW ? SIZE_HAVE_ALL : SIZE_HAVE_HIGH); }
104
105 /* PCAP DVB-CI pseudo-header, see http://www.kaiser.cx/pcap-dvbci.html */
106 #define DVB_CI_PSEUDO_HDR_VER 0
107 #define DVB_CI_PSEUDO_HDR_LEN 4
108 #define DVB_CI_PSEUDO_HDR_CAM_TO_HOST 0xFF
109 #define DVB_CI_PSEUDO_HDR_HOST_TO_CAM 0xFE
110
111
112 /* read a block of data from the camins file and handle the errors */
113 static gboolean
114 read_block(FILE_T fh, guint8 *buf, guint16 buf_len, int *err, gchar **err_info)
115 {
116     int bytes_read;
117
118     bytes_read = file_read((void *)buf, buf_len, fh);
119     if (bytes_read != buf_len) {
120         *err = file_error(fh, err_info);
121         /* bytes_read==0 is end of file */
122         if (bytes_read>0 && *err == 0) {
123             *err = WTAP_ERR_SHORT_READ;
124         }
125         return FALSE;
126     }
127     
128     return TRUE;
129 }
130
131
132 /* find the transaction type for the data bytes of the next packet
133     and the number of data bytes in that packet
134    the fd is moved such that it can be used in a subsequent call
135     to retrieve the data */
136 static gboolean
137 find_next_pkt_dat_type_len(FILE_T fh,
138         guint8 *dat_trans_type, /* transaction type used for the data bytes */
139         guint16 *dat_len,       /* the number of data bytes in the packet */
140         int *err, gchar **err_info)
141 {
142     guint8       block[2];
143     size_read_t  size_stat;
144
145     if (!dat_trans_type || !dat_len)
146         return FALSE;
147
148     RESET_STAT_VALS;
149
150     do {
151         if (read_block(fh, block, sizeof(block), err, err_info) == FALSE) {
152             RESET_STAT_VALS;
153             return FALSE;
154         }
155
156         /* our strategy is to continue reading until we have a high and a
157            low size byte for the same direction, duplicates or spurious data
158            bytes are ignored */
159
160         switch (block[1]) {
161             case TRANS_READ_SIZE_LOW:
162                 if (*dat_trans_type != TRANS_CAM_HOST)
163                     RESET_STAT_VALS;
164                 *dat_trans_type = TRANS_CAM_HOST;
165                 *dat_len |= block[0];
166                 SIZE_ADD_LOW;
167                 break;
168             case TRANS_READ_SIZE_HIGH:
169                 if (*dat_trans_type != TRANS_CAM_HOST)
170                     RESET_STAT_VALS;
171                 *dat_trans_type = TRANS_CAM_HOST;
172                 *dat_len |= (block[0] << 8);
173                 SIZE_ADD_HIGH;
174                 break;
175             case TRANS_WRITE_SIZE_LOW:
176                 if (*dat_trans_type != TRANS_HOST_CAM)
177                     RESET_STAT_VALS;
178                 *dat_trans_type = TRANS_HOST_CAM;
179                 *dat_len |= block[0];
180                 SIZE_ADD_LOW;
181                 break;
182             case TRANS_WRITE_SIZE_HIGH:
183                 if (*dat_trans_type != TRANS_HOST_CAM)
184                     RESET_STAT_VALS;
185                 *dat_trans_type = TRANS_HOST_CAM;
186                 *dat_len |= (block[0] << 8);
187                 SIZE_ADD_HIGH;
188                 break;
189             default:
190                 break;
191         }
192     } while (size_stat != SIZE_HAVE_ALL);
193
194     return TRUE;
195 }
196
197
198 /* buffer allocated by the caller, must be long enough to hold
199    dat_len bytes, ... */
200 static gint
201 read_packet_data(FILE_T fh, guint8 dat_trans_type, guint8 *buf, guint16 dat_len,
202                  int *err, gchar **err_info)
203 {   
204     guint8  *p;
205     guint8   block[2];
206     guint16  bytes_count = 0;
207
208     if (!buf)
209         return -1;
210
211     /* we're not checking for end-of-file here, we read as many bytes as
212        we can get (up to dat_len) and return those
213        end-of-file will be detected when we search for the next packet */
214
215     p = buf;
216     while (bytes_count < dat_len) {
217         if (read_block(fh, block, sizeof(block), err, err_info) == FALSE)
218             break;
219
220         if (block[1] == dat_trans_type) {
221             *p++ = block[0];
222             bytes_count++;
223         }
224         else if (IS_TRANS_SIZE(block[1])) {
225             /* go back before the size transaction block
226                the next packet should be able to pick up this block */
227             if (-1 == file_seek(fh, -(gint64)sizeof(block), SEEK_CUR, err)) 
228                 return -1;
229             break;
230         }
231     }
232
233     return bytes_count;
234 }
235  
236
237 /* create a DVB-CI pseudo header
238    return its length or -1 for error */
239 static gint
240 create_pseudo_hdr(guint8 *buf, guint8 dat_trans_type, guint16 dat_len)
241 {
242     if (!buf)
243         return -1;
244
245     buf[0] = DVB_CI_PSEUDO_HDR_VER;
246
247     if (dat_trans_type==TRANS_CAM_HOST)
248         buf[1] = DVB_CI_PSEUDO_HDR_CAM_TO_HOST;
249     else if (dat_trans_type==TRANS_HOST_CAM)
250         buf[1] = DVB_CI_PSEUDO_HDR_HOST_TO_CAM;
251     else
252         return -1;
253
254     buf[2] = (dat_len>>8) & 0xFF;
255     buf[3] = dat_len & 0xFF;
256
257     return DVB_CI_PSEUDO_HDR_LEN;
258 }
259
260  
261 static gboolean
262 camins_read_packet(FILE_T fh, struct wtap_pkthdr *phdr, Buffer *buf,
263     int *err, gchar **err_info)
264 {
265     guint8      dat_trans_type;
266     guint16     dat_len;
267     guint8     *p;
268     gint        offset, bytes_read;
269
270     if (!find_next_pkt_dat_type_len(fh, &dat_trans_type, &dat_len, err, err_info))
271         return FALSE;
272
273     buffer_assure_space(buf, DVB_CI_PSEUDO_HDR_LEN+dat_len);
274     p = buffer_start_ptr(buf);
275     /* NULL check for p is done in create_pseudo_hdr() */
276     offset = create_pseudo_hdr(p, dat_trans_type, dat_len);
277     if (offset<0) {
278         /* shouldn't happen, all invalid packets must be detected by
279            find_next_pkt_dat_type_len() */
280         *err = WTAP_ERR_INTERNAL;
281         return FALSE;
282     }
283
284     bytes_read = read_packet_data(fh, dat_trans_type,
285             &p[offset], dat_len, err, err_info);
286     /* 0<=bytes_read<=dat_len is very likely a corrupted packet
287        we let the dissector handle this */
288     if (bytes_read < 0)
289         return FALSE;
290     offset += bytes_read;
291
292     phdr->pkt_encap = WTAP_ENCAP_DVBCI;
293     /* timestamps aren't supported for now */
294     phdr->caplen = offset;
295     phdr->len = offset;
296
297     return TRUE;
298 }
299
300
301 static gboolean
302 camins_read(wtap *wth, int *err, gchar **err_info, gint64 *data_offset)
303 {
304     *data_offset = file_tell(wth->fh);
305
306     return camins_read_packet(wth->fh, &wth->phdr, wth->frame_buffer, err,
307         err_info);
308 }
309
310
311 static gboolean
312 camins_seek_read(wtap *wth, gint64 seek_off,
313     struct wtap_pkthdr *pkthdr, Buffer *buf, int length _U_,
314     int *err, gchar **err_info)
315 {
316     if (-1 == file_seek(wth->random_fh, seek_off, SEEK_SET, err))
317         return FALSE;
318
319     return camins_read_packet(wth->random_fh, pkthdr, buf, err, err_info);
320 }
321
322
323
324 int camins_open(wtap *wth, int *err, gchar **err_info _U_)
325 {
326     guint8  found_start_blocks = 0;
327     guint8  count = 0;
328     guint8  block[2];
329     int     bytes_read;
330
331     /* all CAM Inspector files I've looked at have at least two blocks of
332        0x00 0xE1 within the first 20 bytes */
333     do {
334         bytes_read = file_read(block, sizeof(block), wth->fh);
335         if (bytes_read != sizeof(block))
336             break;
337
338         if (block[0]==0x00 && block[1] == 0xE1)
339             found_start_blocks++;
340
341         count++;
342     } while (count<20);
343
344     if (found_start_blocks < 2)
345         return 0;   /* no CAM Inspector file */
346
347     /* rewind the fh so we re-read from the beginning */
348     if (-1 == file_seek(wth->fh, 0, SEEK_SET, err))
349         return -1;
350
351    wth->file_encap = WTAP_ENCAP_DVBCI;
352    wth->snapshot_length = 0;
353    wth->tsprecision = WTAP_FILE_TSPREC_MSEC;
354
355    wth->priv = NULL;
356
357    wth->subtype_read = camins_read;
358    wth->subtype_seek_read = camins_seek_read;
359    wth->file_type = WTAP_FILE_CAMINS;
360
361    *err = 0;
362    return 1;
363 }
364
365
366 /*
367  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
368  *
369  * Local variables:
370  * c-basic-offset: 4
371  * tab-width: 8
372  * indent-tabs-mode: nil
373  * End:
374  *
375  * vi: set shiftwidth=4 tabstop=8 expandtab:
376  * :indentSize=4:tabSize=8:noTabs=true:
377  */