| /* |
| * |
| * 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 <glib.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <netdb.h> |
| #include <sys/time.h> |
| |
| #include <near/nfc_copy.h> |
| |
| #include "nfctool.h" |
| #include "sniffer.h" |
| #include "ndef-decode.h" |
| #include "snep-decode.h" |
| |
| #define SNEP_HEADER_LEN 6 |
| |
| #define SNEP_REQUEST_CONTINUE 0x00 |
| #define SNEP_REQUEST_GET 0x01 |
| #define SNEP_REQUEST_PUT 0x02 |
| #define SNEP_REQUEST_REJECT 0x7f |
| |
| #define SNEP_RESPONSE_CONTINUE 0x80 |
| #define SNEP_RESPONSE_SUCCESS 0x81 |
| #define SNEP_RESPONSE_NOT_FOUND 0xc0 |
| #define SNEP_RESPONSE_EXCESS_DATA 0xc1 |
| #define SNEP_RESPONSE_BAD_REQUEST 0xc2 |
| #define SNEP_RESPONSE_NOT_IMPLEMENTED 0xe0 |
| #define SNEP_RESPONSE_UNSUPPORTED 0xe1 |
| #define SNEP_RESPONSE_REJECT 0xff |
| |
| #define snep_make_frag_index(idx, dir, lsap, rsap) \ |
| ((((idx) << 24) & 0xFF000000) | \ |
| (((dir) << 16) & 0x00FF0000) | \ |
| (((lsap) << 8) & 0x0000FF00) | \ |
| ((rsap) & 0x000000FF)) |
| |
| #define snep_get_frag_index(p) \ |
| snep_make_frag_index((p)->adapter_idx, (p)->direction, \ |
| (p)->llcp.local_sap, (p)->llcp.remote_sap) |
| |
| #define snep_printf_header(fmt, ...) print_indent(SNEP_HEADER_INDENT, \ |
| SNEP_COLOR, fmt, ## __VA_ARGS__) |
| |
| #define snep_printf_msg(fmt, ...) print_indent(SNEP_MSG_INDENT, \ |
| SNEP_COLOR, fmt, ## __VA_ARGS__) |
| |
| #define snep_printf_error(fmt, ...) print_indent(SNEP_MSG_INDENT, COLOR_ERROR, \ |
| fmt, ## __VA_ARGS__) |
| |
| struct snep_frag { |
| guint32 index; |
| guint16 count; |
| guint32 received; |
| guint32 buffer_size; |
| guint8 *buffer; |
| }; |
| |
| static GHashTable *snep_frag_hash = NULL; |
| |
| static void snep_frag_free(struct snep_frag *frag) |
| { |
| g_free(frag->buffer); |
| g_free(frag); |
| } |
| |
| static void snep_frag_delete(guint32 frag_index) |
| { |
| DBG("Deleting frag with index 0x%x", frag_index); |
| |
| g_hash_table_remove(snep_frag_hash, GINT_TO_POINTER(frag_index)); |
| } |
| |
| static void snep_frag_rejected(struct sniffer_packet *packet) |
| { |
| guint32 index; |
| guint8 direction; |
| |
| /* reverse direction to delete the corresponding fragment */ |
| if (packet->direction == NFC_LLCP_DIRECTION_RX) |
| direction = NFC_LLCP_DIRECTION_TX; |
| else |
| direction = NFC_LLCP_DIRECTION_RX; |
| |
| index = snep_make_frag_index(packet->adapter_idx, |
| direction, |
| packet->llcp.local_sap, |
| packet->llcp.remote_sap); |
| |
| snep_frag_delete(index); |
| } |
| |
| static int snep_frag_append(struct snep_frag *frag, |
| struct sniffer_packet *packet) |
| { |
| int err = 0; |
| |
| snep_printf_msg("Ongoing fragmented message"); |
| |
| if (frag->received + packet->llcp.data_len > frag->buffer_size) { |
| snep_printf_error("Too many bytes received"); |
| return -EINVAL; |
| } |
| |
| memcpy(frag->buffer + frag->received, packet->llcp.data, |
| packet->llcp.data_len); |
| |
| frag->received += packet->llcp.data_len; |
| |
| frag->count++; |
| |
| snep_printf_msg("%s fragment #%hu of %u bytes (total %u/%u)", |
| packet->direction == NFC_LLCP_DIRECTION_RX ? "Received" : "Sent", |
| frag->count, packet->llcp.data_len, |
| frag->received, frag->buffer_size); |
| |
| if (frag->received == frag->buffer_size) { |
| snep_printf_msg("End of fragmented message"); |
| |
| err = ndef_print_records(packet->snep.data, |
| packet->snep.data_len); |
| |
| snep_frag_delete(frag->index); |
| } |
| |
| return err; |
| } |
| |
| static int snep_decode_info(struct sniffer_packet *packet) |
| { |
| struct snep_frag *frag; |
| int err; |
| |
| if (packet->snep.data_len <= packet->snep.real_len) { |
| /* Message is not fragmented */ |
| err = ndef_print_records(packet->snep.data, |
| packet->snep.data_len); |
| |
| return err; |
| } |
| |
| frag = g_malloc(sizeof(struct snep_frag)); |
| if (!frag) |
| return -ENOMEM; |
| |
| frag->count = 1; |
| |
| frag->buffer_size = packet->snep.data_len; |
| |
| frag->received = packet->snep.real_len; |
| |
| frag->buffer = g_malloc0(frag->buffer_size); |
| if (!frag->buffer) |
| return -ENOMEM; |
| |
| memcpy(frag->buffer, packet->snep.data, packet->snep.real_len); |
| |
| frag->index = snep_get_frag_index(packet); |
| |
| snep_printf_msg("Start %s fragmented message of %u bytes", |
| packet->direction == NFC_LLCP_DIRECTION_RX ? "Receiving" : "Sending", |
| frag->buffer_size); |
| |
| snep_printf_msg("%s fragment #%hu of %u bytes", |
| packet->direction == NFC_LLCP_DIRECTION_RX ? "Received" : "Sent", |
| frag->count, frag->received); |
| |
| DBG("Adding frag with index 0x%x", frag->index); |
| |
| g_hash_table_replace(snep_frag_hash, GINT_TO_POINTER(frag->index), |
| frag); |
| |
| return 0; |
| } |
| |
| static int snep_decode_req_get(struct sniffer_packet *packet) |
| { |
| guint32 acceptable_len; |
| |
| if (packet->snep.real_len < 4) |
| return -EINVAL; |
| |
| memcpy(&acceptable_len, packet->snep.data, 4); |
| packet->snep.acceptable_len = GUINT_FROM_BE(acceptable_len); |
| |
| packet->snep.data += 4; |
| packet->snep.data_len -= 4; |
| packet->snep.real_len -= 4; |
| |
| return 0; |
| } |
| |
| static int snep_decode_header(struct sniffer_packet *packet) |
| { |
| guint32 data_len; |
| guint8 *data = packet->llcp.data; |
| |
| if (packet->llcp.data_len < SNEP_HEADER_LEN) |
| return -EINVAL; |
| |
| packet->snep.version = data[0]; |
| |
| packet->snep.rcode = data[1]; |
| |
| memcpy(&data_len, data + 2, 4); |
| |
| packet->snep.data_len = GUINT_FROM_BE(data_len); |
| |
| packet->snep.real_len = packet->llcp.data_len - SNEP_HEADER_LEN; |
| |
| if (packet->snep.data_len != 0) |
| packet->snep.data = data + SNEP_HEADER_LEN; |
| |
| return 0; |
| } |
| |
| static void snep_print_version(struct sniffer_packet *packet) |
| { |
| snep_printf_msg("Version: %d.%d", (packet->snep.version & 0xF0) >> 4, |
| packet->snep.version & 0x0F); |
| } |
| |
| static void print_request(struct sniffer_packet *packet, gchar *msg) |
| { |
| snep_print_version(packet); |
| |
| snep_printf_msg("Request: %s", msg); |
| } |
| |
| static void snep_print_response(struct sniffer_packet *packet, gchar *msg) |
| { |
| snep_print_version(packet); |
| |
| snep_printf_msg("Response: %s", msg); |
| } |
| |
| int snep_print_pdu(struct sniffer_packet *packet) |
| { |
| int err = 0; |
| guint32 frag_index; |
| struct snep_frag *frag; |
| |
| snep_printf_header("Simple NDEF Exchange Protocol (SNEP)"); |
| |
| frag_index = snep_get_frag_index(packet); |
| |
| frag = g_hash_table_lookup(snep_frag_hash, GINT_TO_POINTER(frag_index)); |
| |
| if (frag) { |
| /* Incoming or outgoing fragmented message */ |
| err = snep_frag_append(frag, packet); |
| |
| if (err != 0) { |
| snep_printf_error("Error receiving fragmented message"); |
| |
| snep_frag_delete(frag_index); |
| } |
| |
| goto exit; |
| } |
| |
| err = snep_decode_header(packet); |
| if (err != 0) { |
| snep_printf_error("Error decoding message header"); |
| |
| goto exit; |
| } |
| |
| switch (packet->snep.rcode) { |
| /* Requests */ |
| case SNEP_REQUEST_CONTINUE: |
| print_request(packet, "Continue"); |
| break; |
| |
| case SNEP_REQUEST_GET: |
| print_request(packet, "Get"); |
| |
| err = snep_decode_req_get(packet); |
| if (err != 0) |
| goto exit; |
| |
| snep_printf_msg("Acceptable length: %u", |
| packet->snep.acceptable_len); |
| |
| snep_decode_info(packet); |
| break; |
| |
| case SNEP_REQUEST_PUT: |
| print_request(packet, "Put"); |
| |
| snep_decode_info(packet); |
| break; |
| |
| case SNEP_REQUEST_REJECT: |
| print_request(packet, "Reject"); |
| |
| snep_frag_rejected(packet); |
| break; |
| |
| /* Responses */ |
| case SNEP_RESPONSE_CONTINUE: |
| snep_print_response(packet, "Continue"); |
| break; |
| |
| case SNEP_RESPONSE_SUCCESS: |
| snep_print_response(packet, "Success"); |
| |
| if (packet->snep.data_len > 0) |
| snep_decode_info(packet); |
| break; |
| |
| case SNEP_RESPONSE_NOT_FOUND: |
| snep_print_response(packet, "Not Found"); |
| break; |
| |
| case SNEP_RESPONSE_EXCESS_DATA: |
| snep_print_response(packet, "Excess Data"); |
| break; |
| |
| case SNEP_RESPONSE_BAD_REQUEST: |
| snep_print_response(packet, "Bad Request"); |
| break; |
| |
| case SNEP_RESPONSE_NOT_IMPLEMENTED: |
| snep_print_response(packet, "Not Implemented"); |
| break; |
| |
| case SNEP_RESPONSE_UNSUPPORTED: |
| snep_print_response(packet, "Unsupported"); |
| break; |
| |
| case SNEP_RESPONSE_REJECT: |
| snep_print_response(packet, "Reject"); |
| |
| snep_frag_rejected(packet); |
| break; |
| |
| default: |
| snep_printf_error("Invalid request or response code: %d", |
| packet->snep.rcode); |
| break; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| void snep_decode_cleanup(void) |
| { |
| if (snep_frag_hash) |
| g_hash_table_destroy(snep_frag_hash); |
| } |
| |
| int snep_decode_init(void) |
| { |
| snep_frag_hash = |
| g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, |
| (GDestroyNotify)snep_frag_free); |
| |
| return 0; |
| } |