Fix a wrong name in a debug print statement.
[obnox/wireshark/wip.git] / wiretap / btsnoop.c
1 /* btsnoop.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 <errno.h>
27 #include <string.h>
28 #include "wtap-int.h"
29 #include "file_wrappers.h"
30 #include "buffer.h"
31 #include "atm.h"
32 #include "btsnoop.h"
33
34 /*
35  * Symbian's btsnoop format is derived from Sun's snoop format.
36  * See RFC 1761 for a description of the "snoop" file format.
37  */
38
39 /* Magic number in "btsnoop" files. */
40 static const char btsnoop_magic[] = {
41         'b', 't', 's', 'n', 'o', 'o', 'p', '\0'
42 };
43
44 /* "btsnoop" file header (minus magic number). */
45 struct btsnoop_hdr {
46         guint32 version;        /* version number (should be 1) */
47         guint32 datalink;       /* datalink type */
48 };
49
50 /* "btsnoop" record header. */
51 struct btsnooprec_hdr {
52         guint32 orig_len;       /* actual length of packet */
53         guint32 incl_len;       /* number of octets captured in file */
54         guint32 flags;          /* packet flags */
55         guint32 cum_drops;      /* cumulative number of dropped packets */
56         gint64  ts_usec;        /* timestamp microseconds */
57 };
58
59 /* H1 is unframed data with the packet type encoded in the flags field of capture header */
60 /* It can be used for any datalink by placing logging above the datalink layer of HCI */
61 #define KHciLoggerDatalinkTypeH1                1001
62 /* H4 is the serial HCI with packet type encoded in the first byte of each packet */
63 #define KHciLoggerDatalinkTypeH4                1002
64 /* CSR's PPP derived bluecore serial protocol - in practice we log in H1 format after deframing */
65 #define KHciLoggerDatalinkTypeBCSP              1003
66 /* H5 is the official three wire serial protocol derived from BCSP*/
67 #define KHciLoggerDatalinkTypeH5                1004
68
69 #define KHciLoggerHostToController              0
70 #define KHciLoggerControllerToHost              0x00000001
71 #define KHciLoggerACLDataFrame                  0
72 #define KHciLoggerCommandOrEvent                0x00000002
73
74 const gint64 KUnixTimeBase = G_GINT64_CONSTANT(0x00dcddb30f2f8000); /* offset from symbian - unix time */
75
76 static gboolean btsnoop_read(wtap *wth, int *err, gchar **err_info,
77     gint64 *data_offset);
78 static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
79     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
80     int *err, gchar **err_info);
81 static gboolean snoop_read_rec_data(FILE_T fh, guchar *pd, int length, int *err,
82     gchar **err_info);
83
84 int btsnoop_open(wtap *wth, int *err, gchar **err_info)
85 {
86         int bytes_read;
87         char magic[sizeof btsnoop_magic];
88         struct btsnoop_hdr hdr;
89
90         int file_encap=WTAP_ENCAP_UNKNOWN;
91
92         /* Read in the string that should be at the start of a "btsnoop" file */
93         errno = WTAP_ERR_CANT_READ;
94         bytes_read = file_read(magic, sizeof magic, wth->fh);
95         if (bytes_read != sizeof magic) {
96                 *err = file_error(wth->fh, err_info);
97                 if (*err != 0)
98                         return -1;
99                 return 0;
100         }
101         wth->data_offset += sizeof magic;
102
103         if (memcmp(magic, btsnoop_magic, sizeof btsnoop_magic) != 0) {
104                 return 0;
105         }
106
107         /* Read the rest of the header. */
108         errno = WTAP_ERR_CANT_READ;
109         bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
110         if (bytes_read != sizeof hdr) {
111                 *err = file_error(wth->fh, err_info);
112                 if (*err != 0)
113                         return -1;
114                 return 0;
115         }
116         wth->data_offset += sizeof hdr;
117
118         /*
119          * Make sure it's a version we support.
120          */
121         hdr.version = g_ntohl(hdr.version);
122         if (hdr.version != 1) {
123                 *err = WTAP_ERR_UNSUPPORTED;
124                 *err_info = g_strdup_printf("btsnoop: version %u unsupported", hdr.version);
125                 return -1;
126         }
127
128         hdr.datalink = g_ntohl(hdr.datalink);
129         switch (hdr.datalink) {
130         case KHciLoggerDatalinkTypeH1:
131                 file_encap=WTAP_ENCAP_BLUETOOTH_HCI;
132                 break;
133         case KHciLoggerDatalinkTypeBCSP:
134                 *err = WTAP_ERR_UNSUPPORTED;
135                 *err_info = g_strdup_printf("btsnoop: BCSP capture logs unsupported");
136                 return -1;
137         case KHciLoggerDatalinkTypeH5:
138                 *err = WTAP_ERR_UNSUPPORTED;
139                 *err_info = g_strdup_printf("btsnoop: H5 capture logs unsupported");
140                 return -1;
141         case KHciLoggerDatalinkTypeH4:
142                 file_encap=WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR;
143                 break;
144         default:
145                 *err = WTAP_ERR_UNSUPPORTED;
146                 *err_info = g_strdup_printf("btsnoop: datalink type %u unknown or unsupported", hdr.datalink);
147                 return -1;
148         }
149
150         wth->subtype_read = btsnoop_read;
151         wth->subtype_seek_read = btsnoop_seek_read;
152         wth->file_encap = file_encap;
153         wth->snapshot_length = 0;       /* not available in header */
154         wth->tsprecision = WTAP_FILE_TSPREC_USEC;
155         wth->file_type = WTAP_FILE_BTSNOOP;
156         return 1;
157 }
158
159 static gboolean btsnoop_read(wtap *wth, int *err, gchar **err_info,
160     gint64 *data_offset)
161 {
162         guint32 packet_size;
163         guint32 flags;
164         guint32 orig_size;
165         int     bytes_read;
166         struct btsnooprec_hdr hdr;
167         gint64 ts;
168
169         /* As the send/receive flag is stored in the middle of the capture header 
170         but needs to go in the pseudo header for wiretap, the header needs to be reread
171         in the seek_read function*/
172         *data_offset = wth->data_offset;
173
174         /* Read record header. */
175         errno = WTAP_ERR_CANT_READ;
176         bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
177         if (bytes_read != sizeof hdr) {
178                 *err = file_error(wth->fh, err_info);
179                 if (*err == 0 && bytes_read != 0)
180                         *err = WTAP_ERR_SHORT_READ;
181                 return FALSE;
182         }
183         wth->data_offset += sizeof hdr;
184
185         packet_size = g_ntohl(hdr.incl_len);
186         orig_size = g_ntohl(hdr.orig_len);
187         flags = g_ntohl(hdr.flags);
188         if (packet_size > WTAP_MAX_PACKET_SIZE) {
189                 /*
190                  * Probably a corrupt capture file; don't blow up trying
191                  * to allocate space for an immensely-large packet.
192                  */
193                 *err = WTAP_ERR_BAD_RECORD;
194                 *err_info = g_strdup_printf("btsnoop: File has %u-byte packet, bigger than maximum of %u",
195                     packet_size, WTAP_MAX_PACKET_SIZE);
196                 return FALSE;
197         }
198
199         buffer_assure_space(wth->frame_buffer, packet_size);
200         if (!snoop_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
201                 packet_size, err, err_info)) {
202                 return FALSE;   /* Read error */
203         }
204         wth->data_offset += packet_size;
205
206         ts = GINT64_FROM_BE(hdr.ts_usec);
207         ts -= KUnixTimeBase;
208
209         wth->phdr.ts.secs = (guint)(ts / 1000000);
210         wth->phdr.ts.nsecs = (guint)((ts % 1000000) * 1000);
211         wth->phdr.caplen = packet_size;
212         wth->phdr.len = orig_size;
213         if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
214         {
215                 wth->pseudo_header.p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
216         }
217         else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
218         {
219                 wth->pseudo_header.bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
220                 if(flags & KHciLoggerCommandOrEvent)
221                 {
222                         if(wth->pseudo_header.bthci.sent)
223                         {
224                                 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_COMMAND;
225                         }
226                         else
227                         {
228                                 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_EVENT;
229                         }
230                 }
231                 else
232                 {
233                         wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_ACL;
234                 }
235         }
236         return TRUE;
237 }
238
239 static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
240     union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
241     int *err, gchar **err_info) {
242         int     bytes_read;
243         struct btsnooprec_hdr hdr;
244         guint32 flags;
245         if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
246                 return FALSE;
247
248         /* Read record header. */
249         errno = WTAP_ERR_CANT_READ;
250         bytes_read = file_read(&hdr, sizeof hdr, wth->random_fh);
251         if (bytes_read != sizeof hdr) {
252                 *err = file_error(wth->random_fh, err_info);
253                 if (*err == 0 && bytes_read != 0)
254                         *err = WTAP_ERR_SHORT_READ;
255                 return FALSE;
256         }
257         flags = g_ntohl(hdr.flags);
258
259         /*
260          * Read the packet data.
261          */
262         if (!snoop_read_rec_data(wth->random_fh, pd, length, err, err_info))
263                 return FALSE;   /* failed */
264
265         if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
266         {
267                 pseudo_header->p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
268         }
269         else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
270         {
271                 pseudo_header->bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
272                 if(flags & KHciLoggerCommandOrEvent)
273                 {
274                         if(pseudo_header->bthci.sent)
275                         {
276                                 pseudo_header->bthci.channel = BTHCI_CHANNEL_COMMAND;
277                         }
278                         else
279                         {
280                                 pseudo_header->bthci.channel = BTHCI_CHANNEL_EVENT;
281                         }
282                 }
283                 else
284                 {
285                         pseudo_header->bthci.channel = BTHCI_CHANNEL_ACL;
286                 }
287         }
288         return TRUE;
289 }
290
291 static gboolean
292 snoop_read_rec_data(FILE_T fh, guchar *pd, int length, int *err,
293     gchar **err_info)
294 {
295         int     bytes_read;
296
297         errno = WTAP_ERR_CANT_READ;
298         bytes_read = file_read(pd, length, fh);
299
300         if (bytes_read != length) {
301                 *err = file_error(fh, err_info);
302                 if (*err == 0)
303                         *err = WTAP_ERR_SHORT_READ;
304                 return FALSE;
305         }
306         return TRUE;
307 }
308
309 /* Returns 0 if we could write the specified encapsulation type,
310    an error indication otherwise. */
311 int btsnoop_dump_can_write_encap(int encap)
312 {
313     /* Per-packet encapsulations aren't supported. */
314     if (encap == WTAP_ENCAP_PER_PACKET)
315         return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
316
317     /* XXX - for now we only support WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR */
318     if (encap != WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
319         return WTAP_ERR_UNSUPPORTED_ENCAP;
320
321     return 0;
322 }
323
324 struct hci_flags_mapping
325 {
326     guint8 hci_type;
327     guint8 sent;
328     guint8 flags;
329 };
330
331 static const struct hci_flags_mapping hci_flags[] =
332 {
333     { 0x02, TRUE,   KHciLoggerHostToController|KHciLoggerACLDataFrame   }, /* HCI_H4_TYPE_ACL */
334     { 0x02, FALSE,  KHciLoggerControllerToHost|KHciLoggerACLDataFrame   }, /* HCI_H4_TYPE_ACL */
335     { 0x01, TRUE,   KHciLoggerHostToController|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_CMD */
336     { 0x04, FALSE,  KHciLoggerControllerToHost|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_EVT */
337 };
338
339 static guint8 btsnoop_lookup_flags(guint8 hci_type, gboolean sent, guint8 *flags)
340 {
341     guint8 i;
342
343     for (i=0; i < G_N_ELEMENTS(hci_flags); ++i)
344     {
345         if (hci_flags[i].hci_type == hci_type &&
346             hci_flags[i].sent == sent)
347         {
348             *flags = hci_flags[i].flags;
349             return TRUE;
350         }
351     }
352     return FALSE;
353 }
354
355 static gboolean btsnoop_dump_partial_rec_hdr(wtap_dumper *wdh _U_,
356     const struct wtap_pkthdr *phdr,
357     const union wtap_pseudo_header *pseudo_header,
358     const guchar *pd, int *err,
359     struct btsnooprec_hdr *rec_hdr)
360 {
361     gint64 ts_usec;
362     guint8 flags = 0;
363
364     if (!btsnoop_lookup_flags(*pd, pseudo_header->p2p.sent, &flags)) {
365         *err = WTAP_ERR_UNSUPPORTED;
366         return FALSE;
367     }
368
369     ts_usec  = ((gint64) phdr->ts.secs * 1000000) + ((gint64) phdr->ts.nsecs / 1000);
370     ts_usec += KUnixTimeBase;
371
372     rec_hdr->flags = GUINT32_TO_BE(flags);
373     rec_hdr->cum_drops = GUINT32_TO_BE(0);
374     rec_hdr->ts_usec = GINT64_TO_BE(ts_usec);
375
376     return TRUE;
377 }
378
379 /* FIXME: How do we support multiple backends?*/
380 static gboolean btsnoop_dump_h1(wtap_dumper *wdh,
381     const struct wtap_pkthdr *phdr,
382     const union wtap_pseudo_header *pseudo_header,
383     const guchar *pd, int *err)
384 {
385     struct btsnooprec_hdr rec_hdr;
386
387     if (!btsnoop_dump_partial_rec_hdr(wdh, phdr, pseudo_header, pd, err, &rec_hdr))
388         return FALSE;
389
390     rec_hdr.incl_len = GUINT32_TO_BE(phdr->caplen-1);
391     rec_hdr.orig_len = GUINT32_TO_BE(phdr->len-1);
392
393     if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
394         return FALSE;
395
396     wdh->bytes_dumped += sizeof rec_hdr;
397
398     /* Skip HCI packet type */
399     ++pd;
400
401     if (!wtap_dump_file_write(wdh, pd, phdr->caplen-1, err))
402         return FALSE;
403
404     wdh->bytes_dumped += phdr->caplen-1;
405
406     return TRUE;
407 }
408
409 static gboolean btsnoop_dump_h4(wtap_dumper *wdh,
410     const struct wtap_pkthdr *phdr,
411     const union wtap_pseudo_header *pseudo_header,
412     const guchar *pd, int *err)
413 {
414     struct btsnooprec_hdr rec_hdr;
415
416     if (!btsnoop_dump_partial_rec_hdr(wdh, phdr, pseudo_header, pd, err, &rec_hdr))
417         return FALSE;
418
419     rec_hdr.incl_len = GUINT32_TO_BE(phdr->caplen);
420     rec_hdr.orig_len = GUINT32_TO_BE(phdr->len);
421
422     if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
423         return FALSE;
424
425     wdh->bytes_dumped += sizeof rec_hdr;
426
427     if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
428         return FALSE;
429
430     wdh->bytes_dumped += phdr->caplen;
431
432     return TRUE;
433 }
434
435 /* FIXME: How do we support multiple backends?*/
436 gboolean btsnoop_dump_open_h1(wtap_dumper *wdh, int *err)
437 {
438     struct btsnoop_hdr file_hdr;
439
440     /* This is a libpcap file */
441     wdh->subtype_write = btsnoop_dump_h1;
442     wdh->subtype_close = NULL;
443
444     /* Write the file header. */
445     switch (wdh->file_type) {
446
447     case WTAP_FILE_BTSNOOP:
448         wdh->tsprecision = WTAP_FILE_TSPREC_USEC;
449         break;
450
451     default:
452         /* We should never get here - our open routine
453            should only get called for the types above. */
454         *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
455         return FALSE;
456     }
457
458     if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
459         return FALSE;
460
461     wdh->bytes_dumped += sizeof btsnoop_magic;
462
463     /* current "btsnoop" format is 1 */
464     file_hdr.version  = GUINT32_TO_BE(1);
465     /* HCI type encoded in first byte */
466     file_hdr.datalink = GUINT32_TO_BE(KHciLoggerDatalinkTypeH1);
467
468     if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
469         return FALSE;
470
471     wdh->bytes_dumped += sizeof file_hdr;
472
473     return TRUE;
474 }
475
476 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
477    failure */
478 gboolean btsnoop_dump_open_h4(wtap_dumper *wdh, int *err)
479 {
480     struct btsnoop_hdr file_hdr;
481
482     /* This is a libpcap file */
483     wdh->subtype_write = btsnoop_dump_h4;
484     wdh->subtype_close = NULL;
485
486     /* Write the file header. */
487     switch (wdh->file_type) {
488
489     case WTAP_FILE_BTSNOOP:
490         wdh->tsprecision = WTAP_FILE_TSPREC_USEC;
491         break;
492
493     default:
494         /* We should never get here - our open routine
495            should only get called for the types above. */
496         *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
497         return FALSE;
498     }
499
500     if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
501         return FALSE;
502
503     wdh->bytes_dumped += sizeof btsnoop_magic;
504
505     /* current "btsnoop" format is 1 */
506     file_hdr.version  = GUINT32_TO_BE(1);
507     /* HCI type encoded in first byte */
508     file_hdr.datalink = GUINT32_TO_BE(KHciLoggerDatalinkTypeH4);
509
510     if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
511         return FALSE;
512
513     wdh->bytes_dumped += sizeof file_hdr;
514
515     return TRUE;
516 }
517