1 /* packetlogger.c 2 * Routines for opening Apple's (Bluetooth) PacketLogger file format captures 3 * Copyright 2008-2009, Stephen Fisher (see AUTHORS file) 4 * 5 * Wireshark - Network traffic analyzer 6 * By Gerald Combs <gerald@wireshark.org> 7 * Copyright 1998 Gerald Combs 8 * 9 * Based on commview.c, Linux's BlueZ-Gnome Analyzer program and hexdumps of 10 * the output files from Apple's PacketLogger tool. 11 * 12 * SPDX-License-Identifier: GPL-2.0-or-later 13 */ 14 15 #include "config.h" 16 17 #include <stdlib.h> 18 #include <errno.h> 19 #include <string.h> 20 21 #include "wtap-int.h" 22 #include "file_wrappers.h" 23 #include "packetlogger.h" 24 25 typedef struct { 26 gboolean byte_swapped; 27 } packetlogger_t; 28 29 typedef struct packetlogger_header { 30 guint32 len; 31 guint32 ts_secs; 32 guint32 ts_usecs; 33 } packetlogger_header_t; 34 35 /* Packet types. */ 36 #define PKT_HCI_COMMAND 0x00 37 #define PKT_HCI_EVENT 0x01 38 #define PKT_SENT_ACL_DATA 0x02 39 #define PKT_RECV_ACL_DATA 0x03 40 #define PKT_LMP_SEND 0x0A 41 #define PKT_LMP_RECV 0x0B 42 #define PKT_SYSLOG 0xF7 43 #define PKT_KERNEL 0xF8 44 #define PKT_KERNEL_DEBUG 0xF9 45 #define PKT_ERROR 0xFA 46 #define PKT_POWER 0xFB 47 #define PKT_NOTE 0xFC 48 #define PKT_CONFIG 0xFD 49 #define PKT_NEW_CONTROLLER 0xFE 50 51 static gboolean packetlogger_read(wtap *wth, wtap_rec *rec, Buffer *buf, 52 int *err, gchar **err_info, 53 gint64 *data_offset); 54 static gboolean packetlogger_seek_read(wtap *wth, gint64 seek_off, 55 wtap_rec *rec, 56 Buffer *buf, int *err, gchar **err_info); 57 static gboolean packetlogger_read_header(packetlogger_header_t *pl_hdr, 58 FILE_T fh, gboolean byte_swapped, 59 int *err, gchar **err_info); 60 static void packetlogger_byte_swap_header(packetlogger_header_t *pl_hdr); 61 static wtap_open_return_val packetlogger_check_record(wtap *wth, 62 packetlogger_header_t *pl_hdr, 63 int *err, 64 gchar **err_info); 65 static gboolean packetlogger_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, 66 Buffer *buf, int *err, 67 gchar **err_info); 68 69 static int packetlogger_file_type_subtype = -1; 70 71 void register_packetlogger(void); 72 73 /* 74 * Number of packets to try reading. 75 */ 76 #define PACKETS_TO_CHECK 5 77 78 wtap_open_return_val packetlogger_open(wtap *wth, int *err, gchar **err_info) 79 { 80 gboolean byte_swapped = FALSE; 81 packetlogger_header_t pl_hdr; 82 wtap_open_return_val ret; 83 packetlogger_t *packetlogger; 84 85 /* 86 * Try to read the first record. 87 */ 88 if(!packetlogger_read_header(&pl_hdr, wth->fh, byte_swapped, 89 err, err_info)) { 90 /* 91 * Either an immediate EOF or a short read indicates 92 * that the file is probably not a PacketLogger file. 93 */ 94 if (*err != 0 && *err != WTAP_ERR_SHORT_READ) 95 return WTAP_OPEN_ERROR; 96 return WTAP_OPEN_NOT_MINE; 97 } 98 99 /* 100 * If the upper 16 bits of the length are non-zero and the lower 101 * 16 bits are zero, assume the file is byte-swapped from our 102 * byte order. 103 */ 104 if ((pl_hdr.len & 0x0000FFFF) == 0 && 105 (pl_hdr.len & 0xFFFF0000) != 0) { 106 /* 107 * Byte-swap the header. 108 */ 109 packetlogger_byte_swap_header(&pl_hdr); 110 byte_swapped = TRUE; 111 } 112 113 /* 114 * Check whether the first record looks like a PacketLogger 115 * record. 116 */ 117 ret = packetlogger_check_record(wth, &pl_hdr, err, err_info); 118 if (ret != WTAP_OPEN_MINE) { 119 /* 120 * Either we got an error or it's not valid. 121 */ 122 if (ret == WTAP_OPEN_NOT_MINE) { 123 /* 124 * Not valid, so not a PacketLogger file. 125 */ 126 return WTAP_OPEN_NOT_MINE; 127 } 128 129 /* 130 * Error. If it failed with a short read, we don't fail, 131 * so we treat it as a valid file and can then report 132 * it as a truncated file. 133 */ 134 if (*err != WTAP_ERR_SHORT_READ) 135 return WTAP_OPEN_ERROR; 136 } else { 137 /* 138 * Now try reading a few more packets. 139 */ 140 for (int i = 1; i < PACKETS_TO_CHECK; i++) { 141 /* 142 * Read and check the file header; we've already 143 * decided whether this would be a byte-swapped file 144 * or not, so we swap iff we decided it was. 145 */ 146 if (!packetlogger_read_header(&pl_hdr, wth->fh, 147 byte_swapped, err, err_info)) { 148 if (*err == 0) { 149 /* EOF; no more packets to try. */ 150 break; 151 } 152 153 /* 154 * A short read indicates that the file 155 * is probably not a PacketLogger file. 156 */ 157 if (*err != WTAP_ERR_SHORT_READ) 158 return WTAP_OPEN_ERROR; 159 return WTAP_OPEN_NOT_MINE; 160 } 161 162 /* 163 * Check whether this record looks like a PacketLogger 164 * record. 165 */ 166 ret = packetlogger_check_record(wth, &pl_hdr, err, 167 err_info); 168 if (ret != WTAP_OPEN_MINE) { 169 /* 170 * Either we got an error or it's not valid. 171 */ 172 if (ret == WTAP_OPEN_NOT_MINE) { 173 /* 174 * Not valid, so not a PacketLogger 175 * file. 176 */ 177 return WTAP_OPEN_NOT_MINE; 178 } 179 180 /* 181 * Error. If it failed with a short read, 182 * we don't fail, we just stop checking 183 * records, so we treat it as a valid file 184 * and can then report it as a truncated file. 185 */ 186 if (*err != WTAP_ERR_SHORT_READ) 187 return WTAP_OPEN_ERROR; 188 break; 189 } 190 } 191 } 192 193 /* No file header. Reset the fh to 0 so we can read the first packet */ 194 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) 195 return WTAP_OPEN_ERROR; 196 197 /* This is a PacketLogger file */ 198 packetlogger = g_new(packetlogger_t, 1); 199 packetlogger->byte_swapped = byte_swapped; 200 wth->priv = (void *)packetlogger; 201 202 /* Set up the pointers to the handlers for this file type */ 203 wth->subtype_read = packetlogger_read; 204 wth->subtype_seek_read = packetlogger_seek_read; 205 206 wth->file_type_subtype = packetlogger_file_type_subtype; 207 wth->file_encap = WTAP_ENCAP_PACKETLOGGER; 208 wth->file_tsprec = WTAP_TSPREC_USEC; 209 210 /* 211 * Add an IDB; we don't know how many interfaces were 212 * involved, so we just say one interface, about which 213 * we only know the link-layer type, snapshot length, 214 * and time stamp resolution. 215 */ 216 wtap_add_generated_idb(wth); 217 218 return WTAP_OPEN_MINE; /* Our kind of file */ 219 } 220 221 static gboolean 222 packetlogger_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, 223 gchar **err_info, gint64 *data_offset) 224 { 225 *data_offset = file_tell(wth->fh); 226 227 return packetlogger_read_packet(wth, wth->fh, rec, buf, err, err_info); 228 } 229 230 static gboolean 231 packetlogger_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, 232 Buffer *buf, int *err, gchar **err_info) 233 { 234 if(file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) 235 return FALSE; 236 237 if(!packetlogger_read_packet(wth, wth->random_fh, rec, buf, err, err_info)) { 238 if(*err == 0) 239 *err = WTAP_ERR_SHORT_READ; 240 241 return FALSE; 242 } 243 return TRUE; 244 } 245 246 static gboolean 247 packetlogger_read_header(packetlogger_header_t *pl_hdr, FILE_T fh, 248 gboolean byte_swapped, int *err, gchar **err_info) 249 { 250 if (!wtap_read_bytes_or_eof(fh, &pl_hdr->len, 4, err, err_info)) 251 return FALSE; 252 if (!wtap_read_bytes(fh, &pl_hdr->ts_secs, 4, err, err_info)) 253 return FALSE; 254 if (!wtap_read_bytes(fh, &pl_hdr->ts_usecs, 4, err, err_info)) 255 return FALSE; 256 257 /* Convert multi-byte values to host endian */ 258 if (byte_swapped) 259 packetlogger_byte_swap_header(pl_hdr); 260 261 return TRUE; 262 } 263 264 static void 265 packetlogger_byte_swap_header(packetlogger_header_t *pl_hdr) 266 { 267 pl_hdr->len = GUINT32_SWAP_LE_BE(pl_hdr->len); 268 pl_hdr->ts_secs = GUINT32_SWAP_LE_BE(pl_hdr->ts_secs); 269 pl_hdr->ts_usecs = GUINT32_SWAP_LE_BE(pl_hdr->ts_usecs); 270 } 271 272 static wtap_open_return_val 273 packetlogger_check_record(wtap *wth, packetlogger_header_t *pl_hdr, int *err, 274 gchar **err_info) 275 { 276 guint32 length; 277 guint8 type; 278 279 /* Is the header length valid? If not, assume it's not ours. */ 280 if (pl_hdr->len < 8 || pl_hdr->len >= 65536) 281 return WTAP_OPEN_NOT_MINE; 282 283 /* Is the microseconds field of the time stap out of range? */ 284 if (pl_hdr->ts_usecs >= 1000000) 285 return WTAP_OPEN_NOT_MINE; 286 287 /* 288 * If we have any payload, it's a type field; read and check it. 289 */ 290 length = pl_hdr->len - 8; 291 if (length != 0) { 292 /* 293 * Check the type field. 294 */ 295 if (!wtap_read_bytes(wth->fh, &type, 1, err, err_info)) { 296 if (*err != WTAP_ERR_SHORT_READ) 297 return WTAP_OPEN_ERROR; 298 return WTAP_OPEN_NOT_MINE; 299 } 300 301 /* Verify this file belongs to us */ 302 switch (type) { 303 304 case PKT_HCI_COMMAND: 305 case PKT_HCI_EVENT: 306 case PKT_SENT_ACL_DATA: 307 case PKT_RECV_ACL_DATA: 308 case PKT_LMP_SEND: 309 case PKT_LMP_RECV: 310 case PKT_SYSLOG: 311 case PKT_KERNEL: 312 case PKT_KERNEL_DEBUG: 313 case PKT_ERROR: 314 case PKT_POWER: 315 case PKT_NOTE: 316 case PKT_CONFIG: 317 case PKT_NEW_CONTROLLER: 318 break; 319 320 default: 321 return WTAP_OPEN_NOT_MINE; 322 } 323 324 length--; 325 326 if (length != 0) { 327 /* 328 * Now try to read past the rest of the packet bytes; 329 * if that fails with a short read, we don't fail, 330 * so that we can report the file as a truncated 331 * PacketLogger file. 332 */ 333 if (!wtap_read_bytes(wth->fh, NULL, length, 334 err, err_info)) 335 return WTAP_OPEN_ERROR; 336 } 337 } 338 return WTAP_OPEN_MINE; 339 } 340 341 static gboolean 342 packetlogger_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, Buffer *buf, 343 int *err, gchar **err_info) 344 { 345 packetlogger_t *packetlogger = (packetlogger_t *)wth->priv; 346 packetlogger_header_t pl_hdr; 347 348 if(!packetlogger_read_header(&pl_hdr, fh, packetlogger->byte_swapped, 349 err, err_info)) 350 return FALSE; 351 352 if (pl_hdr.len < 8) { 353 *err = WTAP_ERR_BAD_FILE; 354 *err_info = g_strdup_printf("packetlogger: record length %u is too small", pl_hdr.len); 355 return FALSE; 356 } 357 if (pl_hdr.len - 8 > WTAP_MAX_PACKET_SIZE_STANDARD) { 358 /* 359 * Probably a corrupt capture file; don't blow up trying 360 * to allocate space for an immensely-large packet. 361 */ 362 *err = WTAP_ERR_BAD_FILE; 363 *err_info = g_strdup_printf("packetlogger: File has %u-byte packet, bigger than maximum of %u", 364 pl_hdr.len - 8, WTAP_MAX_PACKET_SIZE_STANDARD); 365 return FALSE; 366 } 367 368 rec->rec_type = REC_TYPE_PACKET; 369 rec->block = wtap_block_create(WTAP_BLOCK_PACKET); 370 rec->presence_flags = WTAP_HAS_TS; 371 372 rec->rec_header.packet_header.len = pl_hdr.len - 8; 373 rec->rec_header.packet_header.caplen = pl_hdr.len - 8; 374 375 rec->ts.secs = (time_t)pl_hdr.ts_secs; 376 rec->ts.nsecs = (int)(pl_hdr.ts_usecs * 1000); 377 378 return wtap_read_packet_bytes(fh, buf, rec->rec_header.packet_header.caplen, err, err_info); 379 } 380 381 static const struct supported_block_type packetlogger_blocks_supported[] = { 382 /* 383 * We support packet blocks, with no comments or other options. 384 */ 385 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } 386 }; 387 388 static const struct file_type_subtype_info packetlogger_info = { 389 "macOS PacketLogger", "pklg", "pklg", NULL, 390 FALSE, BLOCKS_SUPPORTED(packetlogger_blocks_supported), 391 NULL, NULL, NULL 392 }; 393 394 void register_packetlogger(void) 395 { 396 packetlogger_file_type_subtype = wtap_register_file_type_subtype(&packetlogger_info); 397 398 /* 399 * Register name for backwards compatibility with the 400 * wtap_filetypes table in Lua. 401 */ 402 wtap_register_backwards_compatibility_lua_name("PACKETLOGGER", 403 packetlogger_file_type_subtype); 404 } 405 406 /* 407 * Editor modelines - https://www.wireshark.org/tools/modelines.html 408 * 409 * Local variables: 410 * c-basic-offset: 8 411 * tab-width: 8 412 * indent-tabs-mode: t 413 * End: 414 * 415 * vi: set shiftwidth=8 tabstop=8 noexpandtab: 416 * :indentSize=8:tabSize=8:noTabs=false: 417 */ 418