Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wiretap/btsnoop.c
Line
Count
Source
1
/* btsnoop.c
2
 *
3
 * Wiretap Library
4
 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5
 *
6
 * SPDX-License-Identifier: GPL-2.0-or-later
7
 */
8
9
#include "config.h"
10
#include "btsnoop.h"
11
12
#include <string.h>
13
#include "wtap_module.h"
14
#include "file_wrappers.h"
15
16
/*
17
 * Symbian's btsnoop format is derived from Sun's snoop format.
18
 * See RFC 1761 for a description of the "snoop" file format.
19
 * See
20
 *
21
 *    https://gitlab.com/wireshark/wireshark/uploads/6d44fa94c164b58516e8577f44a6ccdc/btmodified_rfc1761.txt
22
 *
23
 * for a description of the btsnoop format.
24
 */
25
26
/* Magic number in "btsnoop" files. */
27
static const char btsnoop_magic[] = {
28
    'b', 't', 's', 'n', 'o', 'o', 'p', '\0'
29
};
30
31
/* "btsnoop" file header (minus magic number). */
32
struct btsnoop_hdr {
33
    uint32_t    version;        /* version number (should be 1) */
34
    uint32_t    datalink;       /* datalink type */
35
};
36
37
/* "btsnoop" record header. */
38
struct btsnooprec_hdr {
39
    uint32_t    orig_len;       /* actual length of packet */
40
    uint32_t    incl_len;       /* number of octets captured in file */
41
    uint32_t    flags;          /* packet flags */
42
    uint32_t    cum_drops;      /* cumulative number of dropped packets */
43
    int64_t     ts_usec;        /* timestamp microseconds */
44
};
45
46
/* H1 is unframed data with the packet type encoded in the flags field of capture header */
47
/* It can be used for any datalink by placing logging above the datalink layer of HCI */
48
0
#define KHciLoggerDatalinkTypeH1                1001
49
/* H4 is the serial HCI with packet type encoded in the first byte of each packet */
50
0
#define KHciLoggerDatalinkTypeH4                1002
51
/* CSR's PPP derived bluecore serial protocol - in practice we log in H1 format after deframing */
52
0
#define KHciLoggerDatalinkTypeBCSP              1003
53
/* H5 is the official three wire serial protocol derived from BCSP*/
54
0
#define KHciLoggerDatalinkTypeH5                1004
55
/* Linux Monitor */
56
0
#define KHciLoggerDatalinkLinuxMonitor   2001
57
/* BlueZ 5 Simulator */
58
0
#define KHciLoggerDatalinkBlueZ5Simulator       2002
59
60
0
#define KHciLoggerHostToController              0
61
0
#define KHciLoggerControllerToHost              0x00000001
62
0
#define KHciLoggerACLDataFrame                  0
63
0
#define KHciLoggerCommandOrEvent                0x00000002
64
65
static const int64_t KUnixTimeBase = INT64_C(0x00dcddb30f2f8000); /* offset from symbian - unix time */
66
67
static bool btsnoop_read(wtap *wth, wtap_rec *rec,
68
    int *err, char **err_info, int64_t *offset);
69
static bool btsnoop_seek_read(wtap *wth, int64_t seek_off,
70
    wtap_rec *rec, int *err, char **err_info);
71
static bool btsnoop_read_record(wtap *wth, FILE_T fh,
72
    wtap_rec *rec, int *err, char **err_info);
73
74
static int btsnoop_file_type_subtype = -1;
75
76
void register_btsnoop(void);
77
78
wtap_open_return_val btsnoop_open(wtap *wth, int *err, char **err_info)
79
0
{
80
0
    char magic[sizeof btsnoop_magic];
81
0
    struct btsnoop_hdr hdr;
82
83
0
    int file_encap=WTAP_ENCAP_UNKNOWN;
84
85
    /* Read in the string that should be at the start of a "btsnoop" file */
86
0
    if (!wtap_read_bytes(wth->fh, magic, sizeof magic, err, err_info)) {
87
0
        if (*err != WTAP_ERR_SHORT_READ)
88
0
            return WTAP_OPEN_ERROR;
89
0
        return WTAP_OPEN_NOT_MINE;
90
0
    }
91
92
0
    if (memcmp(magic, btsnoop_magic, sizeof btsnoop_magic) != 0) {
93
0
        return WTAP_OPEN_NOT_MINE;
94
0
    }
95
96
    /* Read the rest of the header. */
97
0
    if (!wtap_read_bytes(wth->fh, &hdr, sizeof hdr, err, err_info))
98
0
        return WTAP_OPEN_ERROR;
99
100
    /*
101
     * Make sure it's a version we support.
102
     */
103
0
    hdr.version = g_ntohl(hdr.version);
104
0
    if (hdr.version != 1) {
105
0
        *err = WTAP_ERR_UNSUPPORTED;
106
0
        *err_info = ws_strdup_printf("btsnoop: version %u unsupported", hdr.version);
107
0
        return WTAP_OPEN_ERROR;
108
0
    }
109
110
0
    hdr.datalink = g_ntohl(hdr.datalink);
111
0
    switch (hdr.datalink) {
112
0
    case KHciLoggerDatalinkTypeH1:
113
0
        file_encap=WTAP_ENCAP_BLUETOOTH_HCI;
114
0
        break;
115
0
    case KHciLoggerDatalinkTypeH4:
116
0
        file_encap=WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR;
117
0
        break;
118
0
    case KHciLoggerDatalinkTypeBCSP:
119
0
        *err = WTAP_ERR_UNSUPPORTED;
120
0
        *err_info = g_strdup("btsnoop: BCSP capture logs unsupported");
121
0
        return WTAP_OPEN_ERROR;
122
0
    case KHciLoggerDatalinkTypeH5:
123
0
        *err = WTAP_ERR_UNSUPPORTED;
124
0
        *err_info = g_strdup("btsnoop: H5 capture logs unsupported");
125
0
        return WTAP_OPEN_ERROR;
126
0
    case KHciLoggerDatalinkLinuxMonitor:
127
0
        file_encap=WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR;
128
0
        break;
129
0
    case KHciLoggerDatalinkBlueZ5Simulator:
130
0
        *err = WTAP_ERR_UNSUPPORTED;
131
0
        *err_info = g_strdup("btsnoop: BlueZ 5 Simulator capture logs unsupported");
132
0
        return WTAP_OPEN_ERROR;
133
0
    default:
134
0
        *err = WTAP_ERR_UNSUPPORTED;
135
0
        *err_info = ws_strdup_printf("btsnoop: datalink type %u unknown or unsupported", hdr.datalink);
136
0
        return WTAP_OPEN_ERROR;
137
0
    }
138
139
0
    wth->subtype_read = btsnoop_read;
140
0
    wth->subtype_seek_read = btsnoop_seek_read;
141
0
    wth->file_encap = file_encap;
142
0
    wth->snapshot_length = 0;   /* not available in header */
143
0
    wth->file_tsprec = WTAP_TSPREC_USEC;
144
0
    wth->file_type_subtype = btsnoop_file_type_subtype;
145
146
    /*
147
     * Add an IDB; we don't know how many interfaces were
148
     * involved, so we just say one interface, about which
149
     * we only know the link-layer type, snapshot length,
150
     * and time stamp resolution.
151
     */
152
0
    wtap_add_generated_idb(wth);
153
154
0
    return WTAP_OPEN_MINE;
155
0
}
156
157
static bool btsnoop_read(wtap *wth, wtap_rec *rec,
158
                             int *err, char **err_info, int64_t *offset)
159
0
{
160
0
    *offset = file_tell(wth->fh);
161
162
0
    return btsnoop_read_record(wth, wth->fh, rec, err, err_info);
163
0
}
164
165
static bool btsnoop_seek_read(wtap *wth, int64_t seek_off,
166
                                  wtap_rec *rec, int *err, char **err_info)
167
0
{
168
0
    if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
169
0
        return false;
170
171
0
    return btsnoop_read_record(wth, wth->random_fh, rec, err, err_info);
172
0
}
173
174
static bool btsnoop_read_record(wtap *wth, FILE_T fh,
175
                                    wtap_rec *rec, int *err, char **err_info)
176
0
{
177
0
    struct btsnooprec_hdr hdr;
178
0
    uint32_t packet_size;
179
0
    uint32_t flags;
180
0
    uint32_t orig_size;
181
0
    int64_t ts;
182
183
    /* Read record header. */
184
185
0
    if (!wtap_read_bytes_or_eof(fh, &hdr, sizeof hdr, err, err_info))
186
0
        return false;
187
188
0
    packet_size = g_ntohl(hdr.incl_len);
189
0
    orig_size = g_ntohl(hdr.orig_len);
190
0
    flags = g_ntohl(hdr.flags);
191
0
    if (packet_size > WTAP_MAX_PACKET_SIZE_STANDARD) {
192
        /*
193
         * Probably a corrupt capture file; don't blow up trying
194
         * to allocate space for an immensely-large packet.
195
         */
196
0
        *err = WTAP_ERR_BAD_FILE;
197
0
        *err_info = ws_strdup_printf("btsnoop: File has %u-byte packet, bigger than maximum of %u",
198
0
                                    packet_size, WTAP_MAX_PACKET_SIZE_STANDARD);
199
0
        return false;
200
0
    }
201
202
0
    ts = GINT64_FROM_BE(hdr.ts_usec);
203
0
    ts -= KUnixTimeBase;
204
205
0
    wtap_setup_packet_rec(rec, wth->file_encap);
206
0
    rec->block = wtap_block_create(WTAP_BLOCK_PACKET);
207
0
    rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
208
0
    rec->ts.secs = (unsigned)(ts / 1000000);
209
0
    rec->ts.nsecs = (unsigned)((ts % 1000000) * 1000);
210
0
    rec->rec_header.packet_header.caplen = packet_size;
211
0
    rec->rec_header.packet_header.len = orig_size;
212
0
    if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
213
0
    {
214
0
        rec->rec_header.packet_header.pseudo_header.p2p.sent = (flags & KHciLoggerControllerToHost) ? false : true;
215
0
    } else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI) {
216
0
        rec->rec_header.packet_header.pseudo_header.bthci.sent = (flags & KHciLoggerControllerToHost) ? false : true;
217
0
        if(flags & KHciLoggerCommandOrEvent)
218
0
        {
219
0
            if(rec->rec_header.packet_header.pseudo_header.bthci.sent)
220
0
            {
221
0
                rec->rec_header.packet_header.pseudo_header.bthci.channel = BTHCI_CHANNEL_COMMAND;
222
0
            }
223
0
            else
224
0
            {
225
0
                rec->rec_header.packet_header.pseudo_header.bthci.channel = BTHCI_CHANNEL_EVENT;
226
0
            }
227
0
        }
228
0
        else
229
0
        {
230
0
            rec->rec_header.packet_header.pseudo_header.bthci.channel = BTHCI_CHANNEL_ACL;
231
0
        }
232
0
    } else  if (wth->file_encap == WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR) {
233
0
        rec->rec_header.packet_header.pseudo_header.btmon.opcode = flags & 0xFFFF;
234
0
        rec->rec_header.packet_header.pseudo_header.btmon.adapter_id = flags >> 16;
235
0
    }
236
237
238
    /* Read packet data. */
239
0
    return wtap_read_bytes_buffer(fh, &rec->data,
240
0
                                  rec->rec_header.packet_header.caplen,
241
0
                                  err, err_info);
242
0
}
243
244
/* Returns 0 if we could write the specified encapsulation type,
245
   an error indication otherwise. */
246
static int btsnoop_dump_can_write_encap(int encap)
247
0
{
248
    /* Per-packet encapsulations aren't supported. */
249
0
    if (encap == WTAP_ENCAP_PER_PACKET)
250
0
        return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
251
252
    /*
253
     * XXX - for now we only support WTAP_ENCAP_BLUETOOTH_HCI,
254
     * WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR, and
255
     * WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR.
256
     */
257
0
    if (encap != WTAP_ENCAP_BLUETOOTH_HCI &&
258
0
        encap != WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR &&
259
0
        encap != WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR)
260
0
        return WTAP_ERR_UNWRITABLE_ENCAP;
261
262
0
    return 0;
263
0
}
264
265
static bool btsnoop_dump(wtap_dumper *wdh, const wtap_rec *rec,
266
    int *err, char **err_info)
267
0
{
268
0
    const union wtap_pseudo_header *pseudo_header = &rec->rec_header.packet_header.pseudo_header;
269
0
    const uint8_t *pd;
270
0
    struct btsnooprec_hdr rec_hdr;
271
0
    uint32_t flags;
272
0
    int64_t nsecs;
273
0
    int64_t ts_usec;
274
275
    /* We can only write packet records. */
276
0
    if (rec->rec_type != REC_TYPE_PACKET) {
277
0
        *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
278
0
        *err_info = wtap_unwritable_rec_type_err_string(rec);
279
0
        return false;
280
0
    }
281
282
    /*
283
     * Make sure this packet doesn't have a link-layer type that
284
     * differs from the one for the file.
285
     */
286
0
    if (wdh->file_encap != rec->rec_header.packet_header.pkt_encap) {
287
0
        *err = WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
288
0
        return false;
289
0
    }
290
291
    /* Don't write out anything bigger than we can read. */
292
0
    if (rec->rec_header.packet_header.caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
293
0
        *err = WTAP_ERR_PACKET_TOO_LARGE;
294
0
        return false;
295
0
    }
296
297
0
    rec_hdr.incl_len = GUINT32_TO_BE(rec->rec_header.packet_header.caplen);
298
0
    rec_hdr.orig_len = GUINT32_TO_BE(rec->rec_header.packet_header.len);
299
300
0
    pd = ws_buffer_start_ptr(&rec->data);
301
302
0
    switch (wdh->file_encap) {
303
304
0
    case WTAP_ENCAP_BLUETOOTH_HCI:
305
0
        switch (pseudo_header->bthci.channel) {
306
307
0
        case BTHCI_CHANNEL_COMMAND:
308
0
            if (!pseudo_header->bthci.sent) {
309
0
                *err = WTAP_ERR_UNWRITABLE_REC_DATA;
310
0
                *err_info = ws_strdup_printf("btsnoop: Command channel, sent false");
311
0
                return false;
312
0
            }
313
0
            flags = KHciLoggerCommandOrEvent|KHciLoggerHostToController;
314
0
            break;
315
316
0
        case BTHCI_CHANNEL_EVENT:
317
0
            if (pseudo_header->bthci.sent) {
318
0
                *err = WTAP_ERR_UNWRITABLE_REC_DATA;
319
0
                *err_info = ws_strdup_printf("btsnoop: Event channel, sent true");
320
0
                return false;
321
0
            }
322
0
            flags = KHciLoggerCommandOrEvent|KHciLoggerControllerToHost;
323
0
            break;
324
325
0
        case BTHCI_CHANNEL_ACL:
326
0
            if (pseudo_header->bthci.sent)
327
0
                flags = KHciLoggerACLDataFrame|KHciLoggerHostToController;
328
0
            else
329
0
                flags = KHciLoggerACLDataFrame|KHciLoggerControllerToHost;
330
0
            break;
331
332
0
        default:
333
0
            *err = WTAP_ERR_UNWRITABLE_REC_DATA;
334
0
            *err_info = ws_strdup_printf("btsnoop: Unknown channel %u",
335
0
                                        pseudo_header->bthci.channel);
336
0
            return false;
337
0
        }
338
0
        break;
339
340
0
    case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
341
0
        if (pseudo_header->p2p.sent)
342
0
            flags = KHciLoggerHostToController;
343
0
        else
344
0
            flags = KHciLoggerControllerToHost;
345
0
        if (rec->rec_header.packet_header.caplen >= 1 &&
346
0
            (pd[0] == 0x01 || pd[0] == 0x04))
347
0
            flags |= KHciLoggerCommandOrEvent;
348
0
        break;
349
350
0
    case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
351
0
        flags = (pseudo_header->btmon.adapter_id << 16) | pseudo_header->btmon.opcode;
352
0
        break;
353
354
0
    default:
355
        /* We should never get here - our open routine should only get
356
           called for the types above. */
357
0
        *err = WTAP_ERR_INTERNAL;
358
0
        *err_info = ws_strdup_printf("btsnoop: invalid encapsulation %u",
359
0
                                    wdh->file_encap);
360
0
        return false;
361
0
    }
362
0
    rec_hdr.flags = GUINT32_TO_BE(flags);
363
0
    rec_hdr.cum_drops = GUINT32_TO_BE(0);
364
365
0
    nsecs = rec->ts.nsecs;
366
0
    ts_usec  = ((int64_t) rec->ts.secs * 1000000) + (nsecs / 1000);
367
0
    ts_usec += KUnixTimeBase;
368
0
    rec_hdr.ts_usec = GINT64_TO_BE(ts_usec);
369
370
0
    if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
371
0
        return false;
372
0
    if (!wtap_dump_file_write(wdh, pd, rec->rec_header.packet_header.caplen, err))
373
0
        return false;
374
0
    return true;
375
0
}
376
377
/* Returns true on success, false on failure; sets "*err" to an error code on
378
   failure */
379
static bool btsnoop_dump_open(wtap_dumper *wdh, int *err, char **err_info _U_)
380
0
{
381
0
    struct btsnoop_hdr file_hdr;
382
0
    uint32_t datalink;
383
384
    /* This is a btsnoop file */
385
0
    wdh->subtype_write = btsnoop_dump;
386
387
0
    switch (wdh->file_encap) {
388
389
0
    case WTAP_ENCAP_BLUETOOTH_HCI:
390
0
        datalink = KHciLoggerDatalinkTypeH1;
391
0
        break;
392
393
0
    case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR:
394
0
        datalink = KHciLoggerDatalinkTypeH4;
395
0
        break;
396
397
0
    case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR:
398
0
        datalink = KHciLoggerDatalinkLinuxMonitor;
399
0
        break;
400
401
0
    default:
402
        /* We should never get here - our open routine should only get
403
           called for the types above. */
404
0
        *err = WTAP_ERR_INTERNAL;
405
0
        *err_info = ws_strdup_printf("btsnoop: invalid encapsulation %u",
406
0
                                    wdh->file_encap);
407
0
        return false;
408
0
    }
409
410
    /* Write the file header. */
411
0
    if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
412
0
        return false;
413
414
    /* current "btsnoop" format is 1 */
415
0
    file_hdr.version  = GUINT32_TO_BE(1);
416
    /* HCI type encoded in first byte */
417
0
    file_hdr.datalink = GUINT32_TO_BE(datalink);
418
419
0
    if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
420
0
        return false;
421
422
0
    return true;
423
0
}
424
425
static const struct supported_block_type btsnoop_blocks_supported[] = {
426
    /*
427
     * We support packet blocks, with no comments or other options.
428
     */
429
    { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED }
430
};
431
432
static const struct file_type_subtype_info btsnoop_info = {
433
    "Symbian OS btsnoop", "btsnoop", "log", NULL,
434
    false, BLOCKS_SUPPORTED(btsnoop_blocks_supported),
435
    btsnoop_dump_can_write_encap, btsnoop_dump_open, NULL
436
};
437
438
void register_btsnoop(void)
439
14
{
440
14
    btsnoop_file_type_subtype = wtap_register_file_type_subtype(&btsnoop_info);
441
442
    /*
443
     * Register name for backwards compatibility with the
444
     * wtap_filetypes table in Lua.
445
     */
446
14
    wtap_register_backwards_compatibility_lua_name("BTSNOOP",
447
14
                                                   btsnoop_file_type_subtype);
448
14
}
449
450
/*
451
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
452
 *
453
 * Local variables:
454
 * c-basic-offset: 4
455
 * tab-width: 8
456
 * indent-tabs-mode: nil
457
 * End:
458
 *
459
 * vi: set shiftwidth=4 tabstop=8 expandtab:
460
 * :indentSize=4:tabSize=8:noTabs=true:
461
 */