| /* |
| * |
| * Near Field Communication nfctool |
| * |
| * Copyright (C) 2013 Intel Corporation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <glib.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #include <near/types.h> |
| |
| #include "nfctool.h" |
| #include "sniffer.h" |
| #include "ndef-decode.h" |
| |
| #define bool_str(b) (b) ? "True" : "False" |
| |
| #define ndef_printf_header(fmt, ...) print_indent(NDEF_HEADER_INDENT, \ |
| NDEF_COLOR, fmt, ## __VA_ARGS__) |
| |
| #define ndef_printf_msg(fmt, ...) print_indent(NDEF_MSG_INDENT, NDEF_COLOR, \ |
| fmt, ## __VA_ARGS__) |
| |
| #define ndef_printf_error(fmt, ...) print_indent(NDEF_MSG_INDENT, COLOR_ERROR, \ |
| fmt, ## __VA_ARGS__) |
| |
| #define ndef_printf_buffer(prefix, buf, buf_len) \ |
| do { \ |
| int i; \ |
| printf("%*c%s%s", NDEF_MSG_INDENT, ' ', \ |
| use_color() ? NDEF_COLOR : "", (prefix)); \ |
| for (i = 0; i < (buf_len); i++) \ |
| printf("%02X ", (buf)[i]); \ |
| printf("\n"); \ |
| } while(0) |
| |
| enum record_tnf { |
| RECORD_TNF_EMPTY = 0x00, |
| RECORD_TNF_WELLKNOWN = 0x01, |
| RECORD_TNF_MIME = 0x02, |
| RECORD_TNF_URI = 0x03, |
| RECORD_TNF_EXTERNAL = 0x04, |
| RECORD_TNF_UNKNOWN = 0x05, |
| RECORD_TNF_UNCHANGED = 0x06, |
| }; |
| |
| static gchar *tnf_str[] = { |
| "Empty (0x00)", |
| "NFC Forum well-known type [NFC RTD] (0x01)", |
| "Media-type as defined in RFC 2046 [RFC 2046] (0x02)", |
| "Absolute URI as defined in RFC 3986 [RFC 3986] (0x03)", |
| "NFC Forum external type [NFC RTD] (0x04)", |
| "Unknown (0x05)", |
| "Unchanged (0x06)", |
| "Reserved (0x07)" |
| }; |
| |
| /* BT EIR list */ |
| #define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ |
| #define EIR_NAME_SHORT 0x08 /* shortened local name */ |
| #define EIR_NAME_COMPLETE 0x09 /* complete local name */ |
| |
| /* Specific OOB EIRs */ |
| #define EIR_CLASS_OF_DEVICE 0x0D /* class of device */ |
| #define EIR_SP_HASH 0x0E /* simple pairing hash C */ |
| #define EIR_SP_RANDOMIZER 0x0F /* simple pairing randomizer R */ |
| /* Optional EIRs */ |
| #define EIR_DEVICE_ID 0x10 /* device ID */ |
| #define EIR_SECURITY_MGR_FLAGS 0x11 /* security manager flags */ |
| |
| static void ndef_print_bt_oob(guint8 *oob_data, guint32 oob_length) |
| { |
| guint32 offset = 0; |
| guint16 length = near_get_le16(oob_data); |
| guint8 *bdaddr, eir_length, eir_type; |
| char *local_name; |
| |
| if (length != oob_length) { |
| ndef_printf_error("Malformed Bluetooth OOB data"); |
| return; |
| } |
| |
| bdaddr = oob_data + 2; |
| offset = 8; |
| |
| ndef_printf_msg("Bluetooth OOB Length: %d", length); |
| ndef_printf_msg("Bluetooth Address: %x:%x:%x:%x:%x:%x", |
| bdaddr[5], bdaddr[4], bdaddr[3], |
| bdaddr[2], bdaddr[1], bdaddr[0]); |
| |
| while (offset < length) { |
| eir_length = oob_data[offset]; |
| eir_type = oob_data[offset + 1]; |
| |
| if (eir_length + offset + 1 > length) |
| break; |
| |
| switch (eir_type) { |
| case EIR_NAME_SHORT: |
| case EIR_NAME_COMPLETE: |
| local_name = g_try_malloc0(eir_length); |
| if (!local_name) |
| break; |
| |
| g_snprintf(local_name, eir_length, |
| "%s", oob_data + offset + 2); |
| |
| ndef_printf_msg("Bluetooth local name: %s", local_name); |
| g_free(local_name); |
| break; |
| |
| case EIR_CLASS_OF_DEVICE: |
| ndef_printf_msg("Bluetooth CoD: 0x%02x-0x%02x-0x%02x", |
| oob_data[offset + 4], |
| oob_data[offset + 3], |
| oob_data[offset + 2]); |
| break; |
| |
| case EIR_SP_HASH: |
| ndef_printf_buffer("Bluetooth Hash: ", |
| oob_data + offset + 2, eir_length - 1); |
| break; |
| |
| case EIR_SP_RANDOMIZER: |
| ndef_printf_buffer("Bluetooth Randomizer: ", |
| oob_data + offset + 2, eir_length - 1); |
| break; |
| |
| default: |
| break; |
| } |
| |
| offset += eir_length + 1; |
| } |
| } |
| |
| #define DE_AUTHENTICATION_TYPE 0x1003 |
| #define DE_NETWORK_KEY 0x1027 |
| #define DE_SSID 0x1045 |
| |
| static void ndef_print_wsc_oob(guint8 *oob_data, guint32 oob_length) |
| { |
| guint32 offset = 0; |
| guint16 de_length, de_type; |
| guint16 auth_type; |
| char *ssid, *passphrase; |
| |
| while (offset < oob_length) { |
| de_type = near_get_be16(oob_data + offset); |
| de_length = near_get_be16(oob_data + offset + 2); |
| |
| switch(de_type) { |
| case DE_AUTHENTICATION_TYPE: |
| auth_type = near_get_be16(oob_data + offset + 4); |
| ndef_printf_msg("WSC Authentication Type: 0x%02x", |
| auth_type); |
| break; |
| |
| case DE_SSID: |
| ssid = g_try_malloc0(de_length + 1); |
| if (!ssid) |
| break; |
| |
| g_snprintf(ssid, de_length + 1, |
| "%s", oob_data + offset + 4); |
| |
| ndef_printf_msg("SSID: %s", ssid); |
| g_free(ssid); |
| break; |
| |
| case DE_NETWORK_KEY: |
| passphrase = g_try_malloc0(de_length + 1); |
| if (!passphrase) |
| break; |
| |
| g_snprintf(passphrase, de_length + 1, |
| "%s", oob_data + offset + 4); |
| |
| ndef_printf_msg("Passphrase: %s", passphrase); |
| g_free(passphrase); |
| break; |
| |
| default: |
| ndef_printf_buffer("Unknown Data Element: ", |
| oob_data + offset + 4, de_length); |
| break; |
| |
| } |
| |
| offset += 4 + de_length; |
| } |
| } |
| |
| int ndef_print_records(guint8 *data, guint32 data_len) |
| { |
| bool mb, me, cf, sr, il; |
| enum record_tnf tnf; |
| char *mime_string; |
| guint8 type_len; |
| guint8 *type; |
| guint32 payload_len; |
| guint8 *payload; |
| guint8 id_len; |
| guint8 *id; |
| guint32 ndef_offset, record_offset; |
| guint8 *record; |
| int err = 0; |
| |
| #define CHECK_OFFSET(s) \ |
| do { \ |
| if (data_len - (ndef_offset + record_offset) < (s)) { \ |
| ndef_printf_error("Malformed NDEF record"); \ |
| sniffer_print_hexdump(stdout, data, data_len, \ |
| NDEF_HEX_INDENT, TRUE); \ |
| err = -EINVAL; \ |
| goto exit; \ |
| } \ |
| } while (0) |
| |
| ndef_offset = 0; |
| |
| while (ndef_offset < data_len) { |
| |
| ndef_printf_header("NDEF Record"); |
| |
| record = data + ndef_offset; |
| record_offset = 0; |
| id_len = 0; |
| type = NULL; |
| id = NULL; |
| payload = NULL; |
| mime_string = NULL; |
| |
| CHECK_OFFSET(2); |
| |
| mb = (record[0] & 0x80) != 0; |
| me = (record[0] & 0x40) != 0; |
| cf = (record[0] & 0x20) != 0; |
| sr = (record[0] & 0x10) != 0; |
| il = (record[0] & 0x08) != 0; |
| tnf = (record[0] & 0x07); |
| |
| type_len = record[1]; |
| |
| record_offset += 2; |
| |
| if (sr) { |
| CHECK_OFFSET(1); |
| |
| payload_len = record[record_offset]; |
| |
| record_offset++; |
| } else { |
| CHECK_OFFSET(4); |
| |
| memcpy(&payload_len, record + record_offset, 4); |
| |
| payload_len = GUINT_FROM_BE(payload_len); |
| |
| record_offset += 4; |
| } |
| |
| if (il) { |
| CHECK_OFFSET(1); |
| |
| id_len = record[record_offset]; |
| |
| record_offset++; |
| } |
| |
| if (type_len > 0) { |
| CHECK_OFFSET(type_len); |
| |
| type = record + record_offset; |
| |
| record_offset += type_len; |
| } |
| |
| if (id_len > 0) { |
| CHECK_OFFSET(id_len); |
| |
| id = record + record_offset; |
| |
| record_offset += id_len; |
| } |
| |
| if (payload_len) { |
| CHECK_OFFSET(payload_len); |
| |
| payload = record + record_offset; |
| |
| record_offset += payload_len; |
| } |
| |
| ndef_printf_msg("Message Begin: %s", bool_str(mb)); |
| ndef_printf_msg("Message End: %s", bool_str(me)); |
| ndef_printf_msg("Chunk Flag: %s", bool_str(cf)); |
| ndef_printf_msg("Short Record: %s", bool_str(sr)); |
| ndef_printf_msg("ID Length present: %s", bool_str(il)); |
| ndef_printf_msg("Type Name Format: %s", tnf_str[tnf]); |
| ndef_printf_msg("Type Length: %u", type_len); |
| ndef_printf_msg("Payload Length: %u", payload_len); |
| |
| if (il) |
| ndef_printf_msg("ID Length: %u", id_len); |
| |
| if (type) { |
| switch (tnf) { |
| case RECORD_TNF_MIME: |
| mime_string = g_try_malloc(type_len + 1); |
| if (mime_string) { |
| g_snprintf(mime_string, |
| type_len + 1, "%s", type); |
| |
| ndef_printf_msg("Type: %s", mime_string); |
| break; |
| } |
| |
| default: |
| ndef_printf_msg("Type:"); |
| sniffer_print_hexdump(stdout, type, type_len, |
| NDEF_HEX_INDENT, FALSE); |
| |
| break; |
| } |
| } |
| |
| if (id) { |
| ndef_printf_msg("ID:"); |
| |
| sniffer_print_hexdump(stdout, id, id_len, |
| NDEF_HEX_INDENT, FALSE); |
| } |
| |
| if (payload) { |
| if (mime_string) { |
| if (strcmp(mime_string, |
| "application/vnd.bluetooth.ep.oob") == 0) |
| ndef_print_bt_oob(payload, payload_len); |
| else if (strcmp(mime_string, |
| "application/vnd.wfa.wsc") == 0) |
| ndef_print_wsc_oob(payload, payload_len); |
| |
| g_free(mime_string); |
| } else { |
| ndef_printf_msg("Payload:"); |
| |
| sniffer_print_hexdump(stdout, payload, |
| payload_len, NDEF_HEX_INDENT, FALSE); |
| } |
| } |
| |
| ndef_offset += record_offset; |
| } |
| |
| exit: |
| return err; |
| |
| #undef CHECK_OFFSET |
| } |