2 * Routines for opening Apple's (Bluetooth) PacketLogger file format captures
3 * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * Based on commview.c, Linux's BlueZ-Gnome Analyzer program and hexdumps of
10 * the output files from Apple's PacketLogger tool.
12 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include "packetlogger.h"
22 #include "file_wrappers.h"
28 typedef struct packetlogger_header
{
32 } packetlogger_header_t
;
35 #define PKT_HCI_COMMAND 0x00
36 #define PKT_HCI_EVENT 0x01
37 #define PKT_SENT_ACL_DATA 0x02
38 #define PKT_RECV_ACL_DATA 0x03
39 #define PKT_SENT_SCO_DATA 0x08
40 #define PKT_RECV_SCO_DATA 0x09
41 #define PKT_LMP_SEND 0x0A
42 #define PKT_LMP_RECV 0x0B
43 #define PKT_SYSLOG 0xF7
44 #define PKT_KERNEL 0xF8
45 #define PKT_KERNEL_DEBUG 0xF9
46 #define PKT_ERROR 0xFA
47 #define PKT_POWER 0xFB
49 #define PKT_CONFIG 0xFD
50 #define PKT_NEW_CONTROLLER 0xFE
52 static bool packetlogger_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
53 int *err
, char **err_info
,
54 int64_t *data_offset
);
55 static bool packetlogger_seek_read(wtap
*wth
, int64_t seek_off
,
57 Buffer
*buf
, int *err
, char **err_info
);
58 static bool packetlogger_read_header(packetlogger_header_t
*pl_hdr
,
59 FILE_T fh
, bool byte_swapped
,
60 int *err
, char **err_info
);
61 static void packetlogger_byte_swap_header(packetlogger_header_t
*pl_hdr
);
62 static wtap_open_return_val
packetlogger_check_record(wtap
*wth
,
63 packetlogger_header_t
*pl_hdr
,
66 static bool packetlogger_read_packet(wtap
*wth
, FILE_T fh
, wtap_rec
*rec
,
67 Buffer
*buf
, int *err
,
70 static int packetlogger_file_type_subtype
= -1;
72 void register_packetlogger(void);
75 * Number of packets to try reading.
77 #define PACKETS_TO_CHECK 5
79 wtap_open_return_val
packetlogger_open(wtap
*wth
, int *err
, char **err_info
)
81 bool byte_swapped
= false;
82 packetlogger_header_t pl_hdr
;
83 wtap_open_return_val ret
;
84 packetlogger_t
*packetlogger
;
87 * Try to read the first record.
89 if(!packetlogger_read_header(&pl_hdr
, wth
->fh
, byte_swapped
,
92 * Either an immediate EOF or a short read indicates
93 * that the file is probably not a PacketLogger file.
95 if (*err
!= 0 && *err
!= WTAP_ERR_SHORT_READ
)
96 return WTAP_OPEN_ERROR
;
97 return WTAP_OPEN_NOT_MINE
;
101 * If the upper 16 bits of the length are non-zero and the lower
102 * 16 bits are zero, assume the file is byte-swapped from our
105 if ((pl_hdr
.len
& 0x0000FFFF) == 0 &&
106 (pl_hdr
.len
& 0xFFFF0000) != 0) {
108 * Byte-swap the header.
110 packetlogger_byte_swap_header(&pl_hdr
);
115 * Check whether the first record looks like a PacketLogger
118 ret
= packetlogger_check_record(wth
, &pl_hdr
, err
, err_info
);
119 if (ret
!= WTAP_OPEN_MINE
) {
121 * Either we got an error or it's not valid.
123 if (ret
== WTAP_OPEN_NOT_MINE
) {
125 * Not valid, so not a PacketLogger file.
127 return WTAP_OPEN_NOT_MINE
;
131 * Error. If it failed with a short read, we don't fail,
132 * so we treat it as a valid file and can then report
133 * it as a truncated file.
135 if (*err
!= WTAP_ERR_SHORT_READ
)
136 return WTAP_OPEN_ERROR
;
139 * Now try reading a few more packets.
141 for (int i
= 1; i
< PACKETS_TO_CHECK
; i
++) {
143 * Read and check the file header; we've already
144 * decided whether this would be a byte-swapped file
145 * or not, so we swap iff we decided it was.
147 if (!packetlogger_read_header(&pl_hdr
, wth
->fh
,
148 byte_swapped
, err
, err_info
)) {
150 /* EOF; no more packets to try. */
155 * A short read indicates that the file
156 * is probably not a PacketLogger file.
158 if (*err
!= WTAP_ERR_SHORT_READ
)
159 return WTAP_OPEN_ERROR
;
160 return WTAP_OPEN_NOT_MINE
;
164 * Check whether this record looks like a PacketLogger
167 ret
= packetlogger_check_record(wth
, &pl_hdr
, err
,
169 if (ret
!= WTAP_OPEN_MINE
) {
171 * Either we got an error or it's not valid.
173 if (ret
== WTAP_OPEN_NOT_MINE
) {
175 * Not valid, so not a PacketLogger
178 return WTAP_OPEN_NOT_MINE
;
182 * Error. If it failed with a short read,
183 * we don't fail, we just stop checking
184 * records, so we treat it as a valid file
185 * and can then report it as a truncated file.
187 if (*err
!= WTAP_ERR_SHORT_READ
)
188 return WTAP_OPEN_ERROR
;
194 /* No file header. Reset the fh to 0 so we can read the first packet */
195 if (file_seek(wth
->fh
, 0, SEEK_SET
, err
) == -1)
196 return WTAP_OPEN_ERROR
;
198 /* This is a PacketLogger file */
199 packetlogger
= g_new(packetlogger_t
, 1);
200 packetlogger
->byte_swapped
= byte_swapped
;
201 wth
->priv
= (void *)packetlogger
;
203 /* Set up the pointers to the handlers for this file type */
204 wth
->subtype_read
= packetlogger_read
;
205 wth
->subtype_seek_read
= packetlogger_seek_read
;
207 wth
->file_type_subtype
= packetlogger_file_type_subtype
;
208 wth
->file_encap
= WTAP_ENCAP_PACKETLOGGER
;
209 wth
->file_tsprec
= WTAP_TSPREC_USEC
;
212 * Add an IDB; we don't know how many interfaces were
213 * involved, so we just say one interface, about which
214 * we only know the link-layer type, snapshot length,
215 * and time stamp resolution.
217 wtap_add_generated_idb(wth
);
219 return WTAP_OPEN_MINE
; /* Our kind of file */
223 packetlogger_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
, int *err
,
224 char **err_info
, int64_t *data_offset
)
226 *data_offset
= file_tell(wth
->fh
);
228 return packetlogger_read_packet(wth
, wth
->fh
, rec
, buf
, err
, err_info
);
232 packetlogger_seek_read(wtap
*wth
, int64_t seek_off
, wtap_rec
*rec
,
233 Buffer
*buf
, int *err
, char **err_info
)
235 if(file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
238 if(!packetlogger_read_packet(wth
, wth
->random_fh
, rec
, buf
, err
, err_info
)) {
240 *err
= WTAP_ERR_SHORT_READ
;
248 packetlogger_read_header(packetlogger_header_t
*pl_hdr
, FILE_T fh
,
249 bool byte_swapped
, int *err
, char **err_info
)
251 if (!wtap_read_bytes_or_eof(fh
, &pl_hdr
->len
, 4, err
, err_info
))
253 if (!wtap_read_bytes(fh
, &pl_hdr
->ts_secs
, 4, err
, err_info
))
255 if (!wtap_read_bytes(fh
, &pl_hdr
->ts_usecs
, 4, err
, err_info
))
258 /* Convert multi-byte values to host endian */
260 packetlogger_byte_swap_header(pl_hdr
);
266 packetlogger_byte_swap_header(packetlogger_header_t
*pl_hdr
)
268 pl_hdr
->len
= GUINT32_SWAP_LE_BE(pl_hdr
->len
);
269 pl_hdr
->ts_secs
= GUINT32_SWAP_LE_BE(pl_hdr
->ts_secs
);
270 pl_hdr
->ts_usecs
= GUINT32_SWAP_LE_BE(pl_hdr
->ts_usecs
);
273 static wtap_open_return_val
274 packetlogger_check_record(wtap
*wth
, packetlogger_header_t
*pl_hdr
, int *err
,
280 /* Is the header length valid? If not, assume it's not ours. */
281 if (pl_hdr
->len
< 8 || pl_hdr
->len
>= 65536)
282 return WTAP_OPEN_NOT_MINE
;
284 /* Is the microseconds field of the time stap out of range? */
285 if (pl_hdr
->ts_usecs
>= 1000000)
286 return WTAP_OPEN_NOT_MINE
;
289 * If we have any payload, it's a type field; read and check it.
291 length
= pl_hdr
->len
- 8;
294 * Check the type field.
296 if (!wtap_read_bytes(wth
->fh
, &type
, 1, err
, err_info
)) {
297 if (*err
!= WTAP_ERR_SHORT_READ
)
298 return WTAP_OPEN_ERROR
;
299 return WTAP_OPEN_NOT_MINE
;
302 /* Verify this file belongs to us */
305 case PKT_HCI_COMMAND
:
307 case PKT_SENT_ACL_DATA
:
308 case PKT_RECV_ACL_DATA
:
309 case PKT_SENT_SCO_DATA
:
310 case PKT_RECV_SCO_DATA
:
315 case PKT_KERNEL_DEBUG
:
320 case PKT_NEW_CONTROLLER
:
324 return WTAP_OPEN_NOT_MINE
;
331 * Now try to read past the rest of the packet bytes;
332 * if that fails with a short read, we don't fail,
333 * so that we can report the file as a truncated
336 if (!wtap_read_bytes(wth
->fh
, NULL
, length
,
338 return WTAP_OPEN_ERROR
;
341 return WTAP_OPEN_MINE
;
345 packetlogger_read_packet(wtap
*wth
, FILE_T fh
, wtap_rec
*rec
, Buffer
*buf
,
346 int *err
, char **err_info
)
348 packetlogger_t
*packetlogger
= (packetlogger_t
*)wth
->priv
;
349 packetlogger_header_t pl_hdr
;
351 if(!packetlogger_read_header(&pl_hdr
, fh
, packetlogger
->byte_swapped
,
355 if (pl_hdr
.len
< 8) {
356 *err
= WTAP_ERR_BAD_FILE
;
357 *err_info
= ws_strdup_printf("packetlogger: record length %u is too small", pl_hdr
.len
);
360 if (pl_hdr
.len
- 8 > WTAP_MAX_PACKET_SIZE_STANDARD
) {
362 * Probably a corrupt capture file; don't blow up trying
363 * to allocate space for an immensely-large packet.
365 *err
= WTAP_ERR_BAD_FILE
;
366 *err_info
= ws_strdup_printf("packetlogger: File has %u-byte packet, bigger than maximum of %u",
367 pl_hdr
.len
- 8, WTAP_MAX_PACKET_SIZE_STANDARD
);
371 rec
->rec_type
= REC_TYPE_PACKET
;
372 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
373 rec
->presence_flags
= WTAP_HAS_TS
;
375 rec
->rec_header
.packet_header
.len
= pl_hdr
.len
- 8;
376 rec
->rec_header
.packet_header
.caplen
= pl_hdr
.len
- 8;
378 rec
->ts
.secs
= (time_t)pl_hdr
.ts_secs
;
379 rec
->ts
.nsecs
= (int)(pl_hdr
.ts_usecs
* 1000);
381 return wtap_read_packet_bytes(fh
, buf
, rec
->rec_header
.packet_header
.caplen
, err
, err_info
);
384 static const struct supported_block_type packetlogger_blocks_supported
[] = {
386 * We support packet blocks, with no comments or other options.
388 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
391 static const struct file_type_subtype_info packetlogger_info
= {
392 "macOS PacketLogger", "pklg", "pklg", NULL
,
393 false, BLOCKS_SUPPORTED(packetlogger_blocks_supported
),
397 void register_packetlogger(void)
399 packetlogger_file_type_subtype
= wtap_register_file_type_subtype(&packetlogger_info
);
402 * Register name for backwards compatibility with the
403 * wtap_filetypes table in Lua.
405 wtap_register_backwards_compatibility_lua_name("PACKETLOGGER",
406 packetlogger_file_type_subtype
);
410 * Editor modelines - https://www.wireshark.org/tools/modelines.html
415 * indent-tabs-mode: t
418 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
419 * :indentSize=8:tabSize=8:noTabs=false: