diff options
author | Arron Wang <arron.wang@intel.com> | 2013-04-03 11:04:46 +0800 |
---|---|---|
committer | Arron Wang <arron.wang@intel.com> | 2013-04-03 11:04:46 +0800 |
commit | 4080c327934424c70492de849d756410d988f5f5 (patch) | |
tree | 333633be9d6f0a2df1d2f1b0ec0b7491fdc4d286 | |
parent | 8004bfc5283ab8e547f1248ccdd5255aae432527 (diff) | |
parent | a2db085bd113f6285431827da60bb60cd410637f (diff) | |
download | neard-4080c327934424c70492de849d756410d988f5f5.tar.gz neard-4080c327934424c70492de849d756410d988f5f5.tar.bz2 neard-4080c327934424c70492de849d756410d988f5f5.zip |
Merge branch 'upstream'
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | Makefile.am | 11 | ||||
-rw-r--r-- | configure.ac | 21 | ||||
-rw-r--r-- | neard.pc.in | 4 | ||||
-rw-r--r-- | plugins/p2p.c | 107 | ||||
-rw-r--r-- | plugins/p2p.h | 1 | ||||
-rw-r--r-- | plugins/snep-validation.c | 2 | ||||
-rw-r--r-- | src/main.c | 14 | ||||
-rw-r--r-- | src/ndef.c | 240 | ||||
-rw-r--r-- | tools/nfctool/llcp-decode.c | 72 | ||||
-rw-r--r-- | tools/nfctool/netlink.c | 9 | ||||
-rw-r--r-- | tools/nfctool/sniffer.c | 21 | ||||
-rw-r--r-- | tools/nfctool/sniffer.h | 4 | ||||
-rw-r--r-- | unit/test-ndef-build.c | 2 | ||||
-rw-r--r-- | unit/test-ndef-parse.c | 8 | ||||
-rw-r--r-- | unit/test-snep-read.c | 961 | ||||
-rw-r--r-- | unit/test-utils.c | 40 | ||||
-rw-r--r-- | unit/test-utils.h | 150 |
18 files changed, 1363 insertions, 306 deletions
@@ -11,3 +11,5 @@ Dorota Moskal <dorota.moskal@tieto.com> Krzysztof Lyczkowski <krzysztof.lyczkowski@tieto.com> Christophe Prigent <christophe.prigent@intel.com> Marcin MaĆagowski <marcin.malagowski@mobica.com> +Guillem Jover <guillem@hadrons.org> +Marcin Malagowski <marcin.malagowski@mobica.com> diff --git a/Makefile.am b/Makefile.am index a276ed3..c4f1346 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,7 @@ src_neard_SOURCES = $(gdbus_sources) $(gweb_sources) $(builtin_sources) \ src/tag.c src/plugin.c src/netlink.c src/ndef.c \ src/tlv.c src/bluetooth.c src/agent.c src/snep.c -src_neard_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @NETLINK_LIBS@ -lresolv -ldl +src_neard_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ @NETLINK_LIBS@ -ldl src_neard_LDFLAGS = -Wl,--export-dynamic @@ -111,7 +111,7 @@ tools_nfctool_nfctool_SOURCES = tools/nfctool/main.c \ tools_nfctool_nfctool_LDADD = @GLIB_LIBS@ @NETLINK_LIBS@ -unit_tests = unit/test-ndef-parse unit/test-ndef-build +unit_tests = unit/test-ndef-parse unit/test-ndef-build unit/test-snep-read unit_test_ndef_parse_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \ src/agent.c src/bluetooth.c \ @@ -123,6 +123,13 @@ unit_test_ndef_build_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \ src/ndef.c unit/test-ndef-build.c unit_test_ndef_build_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ +unit_test_snep_read_SOURCES = $(gdbus_sources) src/log.c src/dbus.c \ + src/agent.c src/bluetooth.c \ + src/ndef.c src/snep.c \ + unit/test-snep-read.c unit/test-utils.c \ + unit/test-utils.h +unit_test_snep_read_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ + noinst_PROGRAMS += $(unit_tests) TESTS = $(unit_tests) diff --git a/configure.ac b/configure.ac index 76d77eb..cb655a9 100644 --- a/configure.ac +++ b/configure.ac @@ -59,34 +59,45 @@ AC_ARG_ENABLE(pie, AC_HELP_STRING([--enable-pie], AC_CHECK_LIB(dl, dlopen, dummy=yes, AC_MSG_ERROR(dynamic linking loader is required)) -PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes, +GLIB_DEPS="glib-2.0 >= 2.28" +PKG_CHECK_MODULES(GLIB, [${GLIB_DEPS}], dummy=yes, AC_MSG_ERROR(GLib >= 2.28 is required)) AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) +AC_SUBST(GLIB_DEPS) -PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.2, dummy=yes, +DBUS_DEPS="dbus-1 >= 1.2" +PKG_CHECK_MODULES(DBUS, [${DBUS_DEPS}], dummy=yes, AC_MSG_ERROR(D-Bus >= 1.2 is required)) AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) +AC_SUBST(DBUS_DEPS) -PKG_CHECK_MODULES(LIBNL3, libnl-3.0 libnl-genl-3.0, [ +LIBNL3_DEPS="libnl-3.0 libnl-genl-3.0" +PKG_CHECK_MODULES(LIBNL3, [${LIBNL3_DEPS}], [ NETLINK_CFLAGS=${LIBNL3_CFLAGS} NETLINK_LIBS=${LIBNL3_LIBS} + NETLINK_DEPS=${LIBNL3_DEPS} ], [ - PKG_CHECK_MODULES(LIBNL2, libnl-2.0, [ + LIBNL2_DEPS="libnl-2.0" + PKG_CHECK_MODULES(LIBNL2, [${LIBNL2_DEPS}], [ NETLINK_CFLAGS=${LIBNL2_CFLAGS} NETLINK_LIBS=${LIBNL2_LIBS} + NETLINK_DEPS=${LIBNL2_DEPS} ], [ - PKG_CHECK_MODULES(LIBNL1, libnl-1, dummy=yes, + LIBNL1_DEPS="libnl-1" + PKG_CHECK_MODULES(LIBNL1, [${LIBNL1_DEPS}], dummy=yes, AC_MSG_ERROR(Netlink library is required)) AC_DEFINE(NEED_LIBNL_COMPAT, 1, [Define to 1 if you need libnl-1 compat functions.]) NETLINK_CFLAGS=${LIBNL1_CFLAGS} NETLINK_LIBS=${LIBNL1_LIBS} + NETLINK_DEPS=${LIBNL1_DEPS} ]) ]) AC_SUBST(NETLINK_CFLAGS) AC_SUBST(NETLINK_LIBS) +AC_SUBST(NETLINK_DEPS) AC_ARG_ENABLE(test, AC_HELP_STRING([--enable-test], [enable test/example scripts]), diff --git a/neard.pc.in b/neard.pc.in index 3a46466..22a494c 100644 --- a/neard.pc.in +++ b/neard.pc.in @@ -7,7 +7,7 @@ plugindir=${libdir}/neard/plugins Name: neard Description: NFC daemon -Requires: glib-2.0 dbus-1 libnl +Requires: @GLIB_DEPS@ @DBUS_DEPS@ @NETLINK_DEPS@ Version: @VERSION@ Libs: -module -avoid-version -export-symbols-regex neard_plugin_desc -Cflags: -I${includedir}
\ No newline at end of file +Cflags: -I${includedir} diff --git a/plugins/p2p.c b/plugins/p2p.c index 378db87..26d9f18 100644 --- a/plugins/p2p.c +++ b/plugins/p2p.c @@ -58,6 +58,57 @@ struct p2p_data { GList *client_list; }; +static int __p2p_bind(struct p2p_data *server_data, GIOFunc listener) +{ + int err, fd = server_data->fd; + struct sockaddr_nfc_llcp addr; + GIOChannel *channel; + struct near_p2p_driver *driver = server_data->driver; + + DBG("Binding %s", driver->name); + + memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp)); + addr.sa_family = AF_NFC; + addr.dev_idx = server_data->adapter_idx; + addr.nfc_protocol = NFC_PROTO_NFC_DEP; + addr.service_name_len = strlen(driver->service_name); + strcpy(addr.service_name, driver->service_name); + + err = bind(fd, (struct sockaddr *) &addr, + sizeof(struct sockaddr_nfc_llcp)); + if (err < 0) { + if (errno == EADDRINUSE) { + DBG("%s is already bound", driver->name); + close(fd); + return 0; + } + + near_error("%s bind failed %d %d", driver->name, err, errno); + close(fd); + return err; + } + + err = listen(fd, 10); + if (err < 0) { + near_error("%s listen failed %d", driver->name, err); + close(fd); + return err; + } + + channel = g_io_channel_unix_new(fd); + g_io_channel_set_close_on_unref(channel, TRUE); + + server_data->watch = g_io_add_watch(channel, + G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, + listener, (gpointer) server_data); + g_io_channel_unref(channel); + + return 0; +} + +static gboolean p2p_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer user_data); + static gboolean p2p_client_event(GIOChannel *channel, GIOCondition condition, gpointer user_data) { @@ -90,6 +141,13 @@ static gboolean p2p_client_event(GIOChannel *channel, GIOCondition condition, g_list_remove(server_data->client_list, client_data); + if (client_data->driver->single_connection) { + server_data->fd = socket(AF_NFC, SOCK_STREAM, + NFC_SOCKPROTO_LLCP); + if (server_data->fd > 0) + __p2p_bind(server_data, p2p_listener_event); + } + g_free(client_data); return FALSE; @@ -206,53 +264,19 @@ static gboolean p2p_listener_event(GIOChannel *channel, GIOCondition condition, server_data->client_list = g_list_append(server_data->client_list, client_data); - return TRUE; + return !driver->single_connection; } static int p2p_bind(struct near_p2p_driver *driver, uint32_t adapter_idx, near_device_io_cb cb) { int err, fd; - struct sockaddr_nfc_llcp addr; - GIOChannel *channel; struct p2p_data *server_data; - DBG("Binding %s", driver->name); - fd = socket(AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP); if (fd < 0) return -errno; - memset(&addr, 0, sizeof(struct sockaddr_nfc_llcp)); - addr.sa_family = AF_NFC; - addr.dev_idx = adapter_idx; - addr.nfc_protocol = NFC_PROTO_NFC_DEP; - addr.service_name_len = strlen(driver->service_name); - strcpy(addr.service_name, driver->service_name); - - err = bind(fd, (struct sockaddr *) &addr, - sizeof(struct sockaddr_nfc_llcp)); - if (err < 0) { - if (errno == EADDRINUSE) { - DBG("%s is already bound", driver->name); - close(fd); - return 0; - } - - near_error("%s bind failed %d %d", driver->name, err, errno); - - close(fd); - return err; - } - - err = listen(fd, 10); - if (err < 0) { - near_error("%s listen failed %d", driver->name, err); - - close(fd); - return err; - } - server_data = g_try_malloc0(sizeof(struct p2p_data)); if (server_data == NULL) { close(fd); @@ -264,14 +288,11 @@ static int p2p_bind(struct near_p2p_driver *driver, uint32_t adapter_idx, server_data->fd = fd; server_data->cb = cb; - channel = g_io_channel_unix_new(fd); - g_io_channel_set_close_on_unref(channel, TRUE); - - server_data->watch = g_io_add_watch(channel, - G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, - p2p_listener_event, - (gpointer) server_data); - g_io_channel_unref(channel); + err = __p2p_bind(server_data, p2p_listener_event); + if (err < 0) { + g_free(server_data); + return err; + } server_list = g_list_append(server_list, server_data); diff --git a/plugins/p2p.h b/plugins/p2p.h index 0b5349c..214dbe3 100644 --- a/plugins/p2p.h +++ b/plugins/p2p.h @@ -27,6 +27,7 @@ struct near_p2p_driver { const char *name; const char *service_name; const char *fallback_service_name; + near_bool_t single_connection; near_bool_t (*read)(int client_fd, uint32_t adapter_idx, uint32_t target_idx, near_device_io_cb cb); diff --git a/plugins/snep-validation.c b/plugins/snep-validation.c index 2094630..682a090 100644 --- a/plugins/snep-validation.c +++ b/plugins/snep-validation.c @@ -152,7 +152,7 @@ static near_bool_t snep_validation_server_req_get(int client_fd, void *data) /* check if the acceptable length is higher than the data_len * otherwise returns a NEAR_SNEP_RESP_EXCESS */ - acceptable_length = GUINT32_FROM_BE(*(uint32_t *)snep_data->nfc_data); + acceptable_length = near_get_be32(snep_data->nfc_data); /* Look if there are some incoming ndef stored */ incoming_ndefs = g_hash_table_lookup(snep_validation_hash, @@ -172,13 +172,6 @@ int main(int argc, char *argv[]) exit(0); } - if (option_detach == TRUE) { - if (daemon(0, 0)) { - perror("Can't start daemon"); - exit(1); - } - } - main_loop = g_main_loop_new(NULL, FALSE); dbus_error_init(&err); @@ -218,6 +211,13 @@ int main(int argc, char *argv[]) __near_plugin_init(option_plugin, option_noplugin); + if (option_detach == TRUE) { + if (daemon(0, 0)) { + perror("Can't start daemon"); + exit(1); + } + } + memset(&sa, 0, sizeof(sa)); sa.sa_handler = sig_term; sigaction(SIGINT, &sa, NULL); @@ -678,41 +678,18 @@ static char *action_to_string(uint8_t action) } } -/** - * @brief returns record type for external type - * Validate type and type length and returns - * type. - * - * @param type Type name in hex format - * @param type_lenth Type name length - * - * @return enum record type - */ - static enum record_type get_external_record_type(uint8_t *type, size_t type_length) { DBG(""); if (strncmp((char *) type, BT_MIME_STRING_2_0, - strlen(BT_MIME_STRING_2_0)) == 0) + sizeof(BT_MIME_STRING_2_0) - 1) == 0) return RECORD_TYPE_MIME_TYPE; else return RECORD_TYPE_UNKNOWN; } -/** - * @brief returns record type - * Validate type name format, type and type length and returns - * type. - * - * @param tnf TypeNameFormat value - * @param type Type name in hex format - * @param type_lenth Type name length - * - * @return enum record type - */ - static enum record_type get_record_type(enum record_tnf tnf, uint8_t *type, size_t type_length) { @@ -855,23 +832,12 @@ static uint8_t validate_record_begin_and_end_bits(uint8_t *msg_mb, return 0; } -/** - * @brief Parse the ndef record header. - * +/* * Parse the ndef record header and cache the begin, end, chunkflag, * short-record and type-name-format bits. ID length and field, record * type, payload length and offset (where payload byte starts in input * parameter). Validate offset for every step forward against total * available length. - * - * @note : Caller responsibility to free the memory. - * - * @param[in] rec ndef byte stream - * @param[in] offset record header offset - * @param[in] length total length in byte stream - * - * @return struct near_ndef_record_header * RecordHeader on Success - * NULL on Failure */ static struct near_ndef_record_header *parse_record_header(uint8_t *rec, uint32_t offset, uint32_t length) @@ -983,18 +949,6 @@ fail: return NULL; } -/** - * @brief Parse the Text record payload - * - * Parse the Text payload. - * - * @param[in] payload NDEF pointer set to record payload first byte - * @param[in] length payload_len - * - * @return struct near_ndef_text_payload * Payload on Success - * NULL on Failure - */ - static struct near_ndef_text_payload * parse_text_payload(uint8_t *payload, uint32_t length) { @@ -1058,18 +1012,6 @@ fail: return NULL; } -/** - * @brief Parse the URI record payload - * - * Parse the URI payload. - * - * @param[in] payload NDEF pointer set to record payload first byte - * @param[in] length Payload length - * - * @return struct near_ndef_uri_payload * payload on Success - * NULL on Failure - */ - static struct near_ndef_uri_payload * parse_uri_payload(uint8_t *payload, uint32_t length) { @@ -1122,16 +1064,10 @@ fail: return NULL; } -/** - * @brief Validate titles records language code in Smartposter. +/* + * Validate titles records language code in Smartposter. * There must not be two or more records with the same language identifier. - * - * @param[in] GSList * list of title records (struct near_ndef_text_payload *) - * - * @return Zero on success - * Negative value on failure */ - static int8_t validate_language_code_in_sp_record(GSList *titles) { uint8_t i, j, length; @@ -1163,21 +1099,6 @@ static int8_t validate_language_code_in_sp_record(GSList *titles) return 0; } -/** - * @brief Parse the smart poster record payload. - * - * Parse the smart poster payload and cache the - * data in respective fields of smart poster structure. - * - * @note Caller responsibility to free the memory. - * - * @param[in] payload NDEF pointer set to record payload first byte - * @param[in] length Record payload length - * - * @return struct near_ndef_sp_payload * Record on Success - * NULL on Failure - */ - static struct near_ndef_sp_payload * parse_sp_payload(uint8_t *payload, uint32_t length) { @@ -1492,28 +1413,7 @@ static uint8_t near_ndef_set_mb_me(uint8_t *hdr, near_bool_t first_rec, return near_ndef_set_me(hdr, last_rec); } -/** - * @brief Allocates ndef message structure - * - * Allocates ndef message structure and fill message header byte, - * type length byte, payload length and type name. Offset is payload - * first byte (caller of this API can start filling their payload - * from offset value). - * - * @note : caller responsibility to free the input and output - * parameters memory. - * - * @param[in] type_name Record type name - * @param[in] payload_len Record payload length - * @param[in] payload_id Record payload id string - * @param[in] payload_id_len Record payload id string length - * @param[in] tnf Type name format to set - * @param[in] first_rec Message begin (MB) flag - * @param[in] last_rec Message end (ME) flag - * - * @return struct near_ndef_message * - Success - * NULL - Failure - */ +/* Caller should put own payload starting from offset value */ static struct near_ndef_message *ndef_message_alloc_complete(char *type_name, uint32_t payload_len, char *payload_id, @@ -1623,8 +1523,6 @@ fail: } /* - * @brief Allocates ndef message structure - * * This is a wrapper to ndef_message_alloc, as, in most cases, * there's no payload id, and MB=TRUE and ME=TRUE. Default type name format * is also set to RECORD_TNF_WELLKNOWN @@ -1776,22 +1674,16 @@ static int near_ndef_prepare_ac_and_cfg_records(enum handover_carrier carrier, struct near_ndef_mime_payload *mime, struct carrier_data *remote_carrier) { - struct carrier_data *local_carrier = NULL; + struct carrier_data *local_carrier; char cdr; char *mime_type, *carrier_string; uint16_t prop; - int err; DBG(""); if (ac == NULL || cfg == NULL) return -EINVAL; - /* to be safe side */ - *ac = NULL; - *cfg = NULL; - carrier_string = NULL; - switch (carrier) { case NEAR_CARRIER_BLUETOOTH: cdr = '0'; @@ -1818,40 +1710,29 @@ static int near_ndef_prepare_ac_and_cfg_records(enum handover_carrier carrier, case NEAR_CARRIER_EMPTY: case NEAR_CARRIER_UNKNOWN: - carrier_string = "Unkknown"; - err = -EINVAL; - goto fail; + default: + return -EINVAL; } if (local_carrier == NULL) { DBG("Unable to retrieve local carrier %s data", carrier_string); - err = -ESRCH; - goto fail; + return -ESRCH; } *cfg = near_ndef_prepare_cfg_message(mime_type, local_carrier->data, local_carrier->size, cdr, 1); - if (*cfg == NULL) { - err = -ENOMEM; - goto fail; - } - *ac = near_ndef_prepare_ac_message(local_carrier->state, cdr); - if (*ac == NULL) { - err = -EINVAL; - goto fail; - } g_free(local_carrier); - return 0; + if (*cfg == NULL || *ac == NULL) { + free_ndef_message(*ac); + free_ndef_message(*cfg); -fail: - g_free(local_carrier); - free_ndef_message(*ac); - free_ndef_message(*cfg); + return -ENOMEM; + } - return err; + return 0; } static void free_ndef_list(gpointer data) @@ -1913,7 +1794,7 @@ static void copy_ac_records(struct near_ndef_message *ho, GList *acs) ac = temp->data; memcpy(ho->data + ho->offset, ac->data, ac->length); /* - * AC records are part of handover message payoad, + * AC records are part of handover message payload, * so modifying offset. */ ho->offset += ac->length; @@ -1937,7 +1818,7 @@ static void copy_cfg_records(struct near_ndef_message *ho, GList *cfgs) memcpy(ho->data + offset, cfg->data, cfg->length); /* * Configuration records (e.g. bt or wifi) records are not part - * of handover payoad, they are consecutive ndef msgs. So + * of handover payload, they are consecutive ndef msgs. So * here we are not modifying ho->offset. */ offset += cfg->length; @@ -2007,7 +1888,7 @@ static struct near_ndef_message *near_ndef_prepare_hs_message( DBG(""); /* - * Preparing empty Hs message incase remote devices has zero + * Preparing empty Hs message in case remote devices has zero * alternative carries or unknown mime types or unknown * configuration data. */ @@ -2037,7 +1918,7 @@ static struct near_ndef_message *near_ndef_prepare_hs_message( } if (g_list_length(ac_msgs) == 0) { - DBG("no alterative carriers, so preparing empty Hs message"); + DBG("no alternative carriers, so preparing empty Hs message"); return near_ndef_prepare_empty_hs_message(); } @@ -2130,7 +2011,7 @@ static struct near_ndef_message *near_ndef_prepare_hr_message(GSList *carriers) DBG(""); - /* Hr message should have atleast one carrier */ + /* Hr message should have at least one carrier */ while (carriers) { ret = near_ndef_prepare_ac_and_cfg_records( string2carrier(carriers->data), @@ -2144,7 +2025,7 @@ static struct near_ndef_message *near_ndef_prepare_hr_message(GSList *carriers) } if (g_list_length(ac_msgs) == 0) { - DBG("no alterative carriers to prepare Hr message"); + DBG("no alternative carriers to prepare Hr message"); goto fail; } @@ -2265,10 +2146,9 @@ fail: } /* - * @brief Parse the Handover request record payload - * This function will parse an Hr record payload, retrieving sub records - * like (ac, cr, er) but it will also get the associated - * ndefs (eg: handover carrier record, mime type for BT) + * This function will parse an handover record payload, retrieving sub records + * like (ac, cr, er) but it will also get the associated ndefs + * (eg: handover carrier record, mime type for BT) * In a handover frame, only the following types are expected: * RECORD_TYPE_WKT_HANDOVER_CARRIER: * RECORD_TYPE_WKT_COLLISION_RESOLUTION @@ -2445,7 +2325,7 @@ static struct near_ndef_ho_payload *parse_ho_payload(enum record_type rec_type, } /* - * Incase of multiple carriers, handover with any carrier + * In case of multiple carriers, handover with any carrier * gets done then leave the loop. */ if (action == TRUE) { @@ -2591,16 +2471,6 @@ uint8_t *near_ndef_data_ptr(struct near_ndef_record *rec) return rec->data; } -/** - * @brief Parse message represented by bytes block -GList *near_ndef_parse(uint8_t *ndef_data, size_t ndef_length, - struct near_ndef_message **reply) - * - * @param[in] ndef_data pointer on data representing ndef message - * @param[in] ndef_length size of ndef_data - * @param[out] records list, contains all the records - * from parsed message - */ GList *near_ndef_parse_msg(uint8_t *ndef_data, size_t ndef_length, struct near_ndef_message **reply) { @@ -2760,11 +2630,9 @@ void near_ndef_records_free(GList *records) } /* - * @brief Compute an NDEF record length - * - * Would compute ndef records length, even though the submitted frame - * is incomplete. This code is used in the handover read function, as - * we have to "guess" the final frame size. + * Compute ndef records length, even though the submitted frame is incomplete. + * This code is used in the handover read function, as we have to "guess" the + * final frame size. * * Message size for SR=1 is: * 1 : ndef rec header (offset 0) @@ -2879,23 +2747,7 @@ fail: return err; } -/** - * @brief Prepare Text ndef record - * - * Prepare text ndef record with provided input data and return - * ndef message structure (length and byte stream) in success or - * NULL in failure case. - * - * @note : caller responsibility to free the input and output - * parameters memory. - * - * @param[in] encoding Encoding (UTF-8 | UTF-16) - * @param[in] language_code Language Code - * @param[in] text Actual text - * - * @return struct near_ndef_message * - Success - * NULL - Failure - */ +/* Possible encoding "UTF-8" or "UTF-16" */ struct near_ndef_message *near_ndef_prepare_text_record(char *encoding, char *language_code, char *text) { @@ -2949,23 +2801,6 @@ fail: return NULL; } -/** - * @brief Prepare URI ndef record - * - * Prepare uri ndef record with provided input data and return - * ndef message structure (length and byte stream) in success or - * NULL in failure case. - * - * @note : caller responsibility to free the input and output - * parameters memory. - * - * @param[in] identifier URI Identifier - * @param[in] field_length URI field length - * @param[in] field URI field - * - * @return struct near_ndef_message * - Success - * NULL - Failure - */ struct near_ndef_message *near_ndef_prepare_uri_record(uint8_t identifier, uint32_t field_length, uint8_t *field) { @@ -3005,23 +2840,6 @@ fail: return NULL; } -/** - * @brief Prepare Smartposter ndef record with mandatory URI fields. - * - * Prepare smartposter ndef record with provided input data and - * return ndef message structure (length and byte stream) in success or - * NULL in failure case. - * - * @note : caller responsibility to free the input and output - * parameters memory. - * - * @param[in] uri_identfier - * @param[in] uri_field_length - * @param[in] uri_field - * - * @return struct near_ndef_message * - Success - * NULL - Failure - */ struct near_ndef_message * near_ndef_prepare_smartposter_record(uint8_t uri_identifier, uint32_t uri_field_length, diff --git a/tools/nfctool/llcp-decode.c b/tools/nfctool/llcp-decode.c index 30f9aa9..aa701c7 100644 --- a/tools/nfctool/llcp-decode.c +++ b/tools/nfctool/llcp-decode.c @@ -275,31 +275,66 @@ static int llcp_print_sequence(guint8 *data, guint32 data_len) return 0; } -static int llcp_print_agf(guint8 *data, guint8 data_len, - struct timeval *timestamp) +static int llcp_print_agf(guint8 *data, guint32 data_len, guint adapter_idx, + guint8 direction, struct timeval *timestamp) { - guint16 len; - guint16 offset = 0; + guint8 *pdu; + gsize pdu_size; + gsize size; + guint16 offset; + guint16 count; + int err; - if (data_len < 2) + if (data_len < 2) { + print_error("Error parsing AGF PDU"); return -EINVAL; + } + + printf("\n"); + + pdu = NULL; + pdu_size = 0; + offset = 0; + count = 0; while (offset < data_len - 2) { - len = (data[offset] << 8) | data[offset + 1]; + size = (data[offset] << 8) | data[offset + 1]; + offset += 2; - if (offset + len > data_len) - return -EINVAL; + if (size == 0 || offset + size > data_len) { + print_error("Error parsing AGF PDU"); + err = -EINVAL; + goto exit; + } + + if (size + NFC_LLCP_RAW_HEADER_SIZE > pdu_size) { + pdu_size = size + NFC_LLCP_RAW_HEADER_SIZE; + pdu = g_realloc(pdu, pdu_size); + + pdu[0] = adapter_idx; + pdu[1] = direction; + } + + memcpy(pdu + NFC_LLCP_RAW_HEADER_SIZE, data + offset, size); + + printf("-- AGF LLC PDU %02u:\n", count++); - llcp_print_pdu(data + offset, len, timestamp); + llcp_print_pdu(pdu, size + NFC_LLCP_RAW_HEADER_SIZE, timestamp); - offset += len; + offset += size; } - return 0; + printf("-- End of AGF LLC PDUs\n"); + + err = 0; +exit: + g_free(pdu); + + return err; } -static int llcp_print_dm(guint8 *data, guint8 data_len) +static int llcp_print_dm(guint8 *data, guint32 data_len) { gchar *reason; @@ -347,17 +382,17 @@ static int llcp_print_dm(guint8 *data, guint8 data_len) return 0; } -static int llcp_print_i(guint8 *data, guint8 data_len) +static int llcp_print_i(guint8 *data, guint32 data_len) { if (llcp_print_sequence(data, data_len)) return -EINVAL; - sniffer_print_hexdump(stdout, data + 1, data_len - 1, " "); + sniffer_print_hexdump(stdout, data + 1, data_len - 1, " ", TRUE); return 0; } -static int llcp_print_frmr(guint8 *data, guint8 data_len) +static int llcp_print_frmr(guint8 *data, guint32 data_len) { guint8 val; @@ -436,8 +471,8 @@ int llcp_print_pdu(guint8 *data, guint32 data_len, struct timeval *timestamp) switch (llcp.ptype) { case LLCP_PTYPE_AGF: - llcp_print_agf(llcp.data, llcp.data_len, timestamp); - printf(" End of AGF frame\n"); + llcp_print_agf(llcp.data, llcp.data_len, + adapter_idx, direction, timestamp); break; case LLCP_PTYPE_I: @@ -465,7 +500,8 @@ int llcp_print_pdu(guint8 *data, guint32 data_len, struct timeval *timestamp) break; default: - sniffer_print_hexdump(stdout, llcp.data, llcp.data_len, " "); + sniffer_print_hexdump(stdout, llcp.data, llcp.data_len, " ", + TRUE); break; } diff --git a/tools/nfctool/netlink.c b/tools/nfctool/netlink.c index dd0bef6..1d4e2bf 100644 --- a/tools/nfctool/netlink.c +++ b/tools/nfctool/netlink.c @@ -671,6 +671,7 @@ static int nl_nfc_event_cb(struct nl_msg *n, void *arg) guint32 idx = INVALID_ADAPTER_IDX; struct nlattr *attr[NFC_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(n)); + guint32 cmd = gnlh->cmd; nfc_event_cb_t cb = NULL; DBG("Received cmd %d", gnlh->cmd); @@ -682,12 +683,12 @@ static int nl_nfc_event_cb(struct nl_msg *n, void *arg) idx = nla_get_u32(attr[NFC_ATTR_DEVICE_INDEX]); if (handlers != NULL) - cb = g_hash_table_lookup(handlers, GINT_TO_POINTER(gnlh->cmd)); + cb = g_hash_table_lookup(handlers, GINT_TO_POINTER(cmd)); if (cb == NULL) return NL_SKIP; - switch (gnlh->cmd) { + switch (cmd) { case NFC_EVENT_LLC_SDRES: DBG("Received NFC_EVENT_LLC_SDRES\n"); nl_nfc_send_sdres_event(idx, attr[NFC_ATTR_LLC_SDP], cb); @@ -726,8 +727,10 @@ static gboolean nl_gio_handler(GIOChannel *channel, void nl_add_event_handler(guint8 cmd, nfc_event_cb_t cb) { + guint32 c = cmd; + if (handlers != NULL) - g_hash_table_replace(handlers, GINT_TO_POINTER(cmd), cb); + g_hash_table_replace(handlers, GINT_TO_POINTER(c), cb); } void nl_cleanup(void) diff --git a/tools/nfctool/sniffer.c b/tools/nfctool/sniffer.c index c48e053..2e89ddc 100644 --- a/tools/nfctool/sniffer.c +++ b/tools/nfctool/sniffer.c @@ -157,14 +157,14 @@ static void pcap_file_cleanup(void) * 00000000: 01 01 43 20 30 70 72 6F 70 65 72 74 69 65 73 20 |..C 0properties | * */ -void sniffer_print_hexdump(FILE *file, unsigned char *data, int len, - char *line_prefix) +void sniffer_print_hexdump(FILE *file, guint8 *data, guint32 len, + gchar *line_prefix, gboolean print_len) { - int digits; - int offset; - int total; - char line[LINE_SIZE]; - char *hexa = NULL, *human = NULL; + guint8 digits; + guint32 offset; + guint32 total; + gchar line[LINE_SIZE]; + gchar *hexa = NULL, *human = NULL; if (len <= 0) return; @@ -173,6 +173,13 @@ void sniffer_print_hexdump(FILE *file, unsigned char *data, int len, digits = 0; total = 0; + if (print_len) { + if (line_prefix) + fprintf(file, "%s", line_prefix); + + fprintf(file, "Total length: %u bytes\n", len); + } + while (total < len) { if (digits == 0) { memset(line, ' ', HUMAN_READABLE_OFFSET); diff --git a/tools/nfctool/sniffer.h b/tools/nfctool/sniffer.h index cfa0eb4..2f3603a 100644 --- a/tools/nfctool/sniffer.h +++ b/tools/nfctool/sniffer.h @@ -30,7 +30,7 @@ int sniffer_init(void); void sniffer_cleanup(void); -void sniffer_print_hexdump(FILE *file, unsigned char *data, int len, - char *line_prefix); +void sniffer_print_hexdump(FILE *file, guint8 *data, guint32 len, + gchar *line_prefix, gboolean print_len); #endif /* __SNIFFER_H */ diff --git a/unit/test-ndef-build.c b/unit/test-ndef-build.c index 777d11d..a658224 100644 --- a/unit/test-ndef-build.c +++ b/unit/test-ndef-build.c @@ -54,7 +54,7 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - g_test_add_func("/testndefbuild/Test Text NDEF", test_ndef_text_build); + g_test_add_func("/testNDEF-build/Test Text NDEF", test_ndef_text_build); return g_test_run(); } diff --git a/unit/test-ndef-parse.c b/unit/test-ndef-parse.c index ec3607f..bfabfdf 100644 --- a/unit/test-ndef-parse.c +++ b/unit/test-ndef-parse.c @@ -306,11 +306,11 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - g_test_add_func("/testndefparse/Test URI NDEF", test_ndef_uri); - g_test_add_func("/testndefparse/Test Text NDEF", test_ndef_text); - g_test_add_func("/testndefparse/Test Single record SmartPoster NDEF", + g_test_add_func("/testNDEF-parse/Test URI NDEF", test_ndef_uri); + g_test_add_func("/testNDEF-parse/Test Text NDEF", test_ndef_text); + g_test_add_func("/testNDEF-parse/Test Single record SmartPoster NDEF", test_ndef_single_sp); - g_test_add_func("/testndefparse/Test Title record SmartPoster NDEF", + g_test_add_func("/testNDEF-parse/Test Title record SmartPoster NDEF", test_ndef_title_sp); return g_test_run(); diff --git a/unit/test-snep-read.c b/unit/test-snep-read.c new file mode 100644 index 0000000..c008376 --- /dev/null +++ b/unit/test-snep-read.c @@ -0,0 +1,961 @@ +/* + * neard - Near Field Communication manager + * + * Copyright (C) 2013 Mobica Limited. 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <linux/socket.h> + +#include <near/types.h> + +#include "test-utils.h" + +#define TEST_SNEP_LOG(fmt, ...) do { \ + if (g_test_verbose()) {\ + g_printf("[SNEP unit] " fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define TEST_SNEP_PTR_UNUSED(x) do { if ((void *)x != NULL) {} } while (0) + +static const char *short_text = "neard"; + +static const char *long_text = "The Linux NFC project aims to provide a " \ + "full NFC support for Linux. It is based on the neard NFC user space "\ + "stack running on top of the Linux kernel NFC subsystem. NFC stands " \ + "Near Field Communication. It is a short-range (a few centimeters)"\ + "radio technology that enables communication between devices that " \ + "either touch or are momentarily held close together. NFC is an open "\ + "technology standardized by the NFC Forum. It is based on RFID. "; + +/* 'neard' - UTF-8 - en-US Text NDEF */ +static uint8_t text[] = { 0xd1, 0x1, 0xb, 0x54, 0x5, 0x65, 0x6e, + 0x2d, 0x55, 0x53, 0x6e, 0x65, 0x61, 0x72, 0x64 }; + +/* sockets */ +static int sockfd[2]; +static int client; +static int server; + +struct test_snep_context { + uint8_t snep_version; + uint32_t req_info_len; + uint32_t payload_len; + uint8_t *req_info; + + uint32_t acc_len; /* req GET specific */ + + struct near_ndef_message *test_recd_msg; +}; + +/* variables used in dummy_req_{get|put} */ +static struct test_snep_context *gcontext; +static struct near_ndef_record *stored_recd; +static GSList *test_fragments; + +/* GET/PUT server functions */ + +/* + * @brief Utility: Dummy PUT request handler + */ +static near_bool_t test_snep_dummy_req_put(int fd, void *data) +{ + struct p2p_snep_data *snep_data = data; + GList *records; + uint8_t *nfc_data; + uint32_t nfc_data_length; + uint32_t offset = 0; + + TEST_SNEP_LOG(">> dummy_req_put entry %p\n", data); + + if (snep_data == NULL) + goto error; + + if (stored_recd) + TEST_SNEP_LOG("\tdummy_req_put already stored record\n"); + + test_fragments = g_slist_append(test_fragments, snep_data); + + if (snep_data->nfc_data_length > snep_data->nfc_data_current_length) + return TRUE; + + nfc_data_length = 0; + nfc_data = g_try_malloc0(snep_data->nfc_data_length); + g_assert(nfc_data != NULL); + + while (g_slist_length(test_fragments) > 0) { + static int frag_cnt; + struct p2p_snep_data *fragment = test_fragments->data; + + TEST_SNEP_LOG("\tdummy_req_put frag=%d, len=%d, current=%d\n", + frag_cnt, fragment->nfc_data_length, + fragment->nfc_data_current_length); + test_fragments = g_slist_remove(test_fragments, fragment); + + memcpy(nfc_data + offset, fragment->nfc_data, + fragment->nfc_data_current_length - nfc_data_length); + + offset += fragment->nfc_data_current_length - nfc_data_length; + nfc_data_length = offset; + + frag_cnt++; + } + + records = near_ndef_parse_msg(nfc_data, nfc_data_length, NULL); + if (records == NULL) { + TEST_SNEP_LOG("\tdummy_req_put parsing ndef failed\n"); + goto error; + } + + if (g_list_length(records) != 1) { + TEST_SNEP_LOG("\tdummy_req_put records number mismatch"); + goto error; + } + + g_free(nfc_data); + + stored_recd = records->data; + + TEST_SNEP_LOG("\t\tdummy_req_put STORED REC data=%p length=%zu\n", + stored_recd->data, stored_recd->data_len); + + near_snep_core_response_noinfo(fd, NEAR_SNEP_RESP_SUCCESS); + return TRUE; + +error: + TEST_SNEP_LOG("\tdummy_req_put error!!!\n"); + return FALSE; +} + +/* + * @brief Utility: Dummy GET request handler + */ +static near_bool_t test_snep_dummy_req_get(int fd, void *data) +{ + struct p2p_snep_data *snep_data = data; + + TEST_SNEP_LOG(">> dummy_req_get entry %p\n", data); + + if (snep_data == NULL) + goto error; + + TEST_SNEP_LOG("\t\tdummy_req_get STORED REC data=%p length=%zu\n", + stored_recd->data, stored_recd->data_len); + + near_snep_core_response_with_info(fd, NEAR_SNEP_RESP_SUCCESS, + near_ndef_data_ptr(stored_recd), + near_ndef_data_length(stored_recd)); + return TRUE; + +error: + TEST_SNEP_LOG("\tdummy_req_get error!!!\n"); + return FALSE; +} + +static void test_snep_init(gpointer context, gconstpointer data) +{ + struct test_snep_context *ctx = context; + struct timeval tv; + int ret; + const char *test_data = data; + + g_assert(socketpair(PF_LOCAL, SOCK_STREAM, 0, sockfd) == 0); + + client = 0; + server = 1; + + tv.tv_sec = 1; + tv.tv_usec = 0; + ret = setsockopt(sockfd[client], SOL_SOCKET, SO_RCVTIMEO, + (const void *) &tv, sizeof(tv)); + if (ret != 0) + TEST_SNEP_LOG("set sock SO_RCVTIMEO failed"); + + __near_snep_core_init(); + + stored_recd = NULL; + + ctx->test_recd_msg = test_ndef_create_test_record(test_data); + + ctx->snep_version = NEAR_SNEP_VERSION; + ctx->req_info = ctx->test_recd_msg->data; + ctx->req_info_len = ctx->test_recd_msg->length; + ctx->payload_len = ctx->test_recd_msg->length; + ctx->acc_len = 64; + + gcontext = ctx; +} + +static void test_snep_cleanup(gpointer context, gconstpointer data) +{ + struct test_snep_context *ctx = context; + + __near_snep_core_cleanup(); + + if (stored_recd) + test_ndef_free_record(stored_recd); + + if (ctx->test_recd_msg) { + g_free(ctx->test_recd_msg->data); + g_free(ctx->test_recd_msg); + } + + g_slist_free(test_fragments); + + close(sockfd[client]); + close(sockfd[server]); + + gcontext = NULL; +} + +/* + * @brief Utility: Allocate and build SNEP request frame. + * + * @param[in] frame_len Size of the entire frame + * @param[in] ver SNEP protocol version field + * @param[in] resp_type SNEP response code field + * @param[in] info_len SNEP info length field + * @param[in] data SNEP info field + * @param[in] payload_len Size of the payload to be inserted + * @return p2p_snep_resp_frame + */ +static struct p2p_snep_req_frame *test_snep_build_req_frame( + size_t frame_len, uint8_t ver, uint8_t req_type, + uint32_t info_len, uint8_t *data, uint32_t payload_len) +{ + struct p2p_snep_req_frame *req; + + req = g_try_malloc0(frame_len); + g_assert(req != NULL); + + req->version = ver; + req->request = req_type; + req->length = GUINT_TO_BE(info_len); + memcpy(req->ndef, data, payload_len); + + return req; +} + +/* + * @brief Utility: Allocate and build SNEP GET request frame. + * + * @param[in] frame_len Size of the entire frame + * @param[in] ver SNEP protocol version field + * @param[in] resp_type SNEP response code field + * @param[in] info_len SNEP info length field + * @param[in] data SNEP info field + * @param[in] acc_len SNEP acceptable length field + * @param[in] payload_len Size of the payload to be inserted + * @return p2p_snep_resp_frame + */ +static struct p2p_snep_req_frame *test_snep_build_req_get_frame( + size_t frame_len, uint8_t ver, uint8_t req_type, + uint32_t info_len, uint32_t acc_len, uint8_t *data, + uint32_t payload_len) +{ + struct p2p_snep_req_frame *req; + uint32_t acc_len_be = GUINT_TO_BE(acc_len); + + req = g_try_malloc0(frame_len); + g_assert(req != NULL); + + req->version = ver; + req->request = req_type; + req->length = GUINT_TO_BE(info_len); + memcpy(req->ndef, &acc_len_be, sizeof(acc_len_be)); + memcpy(req->ndef + sizeof(acc_len_be), data, payload_len); + + return req; +} + +/* + * @brief Utility: Allocate and build SNEP response frame. + * + * @param[in] frame_len Size of the entire frame + * @param[in] ver SNEP protocol version field + * @param[in] resp_type SNEP response code field + * @param[in] info_len SNEP info length field + * @param[in] data SNEP info field + * @return p2p_snep_resp_frame + */ +static struct p2p_snep_resp_frame *test_snep_build_resp_frame( + size_t frame_len, uint8_t ver, uint8_t resp_type, + uint32_t info_len, uint8_t *data) +{ + struct p2p_snep_resp_frame *resp; + + resp = g_try_malloc0(frame_len); + g_assert(resp != NULL); + + resp->version = ver; + resp->response = resp_type; + resp->length = GUINT_TO_BE(info_len); + memcpy(resp->info, data, info_len); + + return resp; +} + +/* + * @brief Utility: Send the \p req frame to the socket and call + * near_snep_core_read + * + * @param[in] req Request frame to send + * @param[in] frame_len Size of the frame + * @param[in] req_get GET server function + * @param[in] req_put PUT server function + * @return near_bool_t returned by near_snep_core_read + */ +static near_bool_t test_snep_read_req_common( + struct p2p_snep_req_frame *req, size_t frame_len, + near_server_io req_get, near_server_io req_put) +{ + near_bool_t ret; + size_t nbytes; + + nbytes = send(sockfd[client], req, frame_len, 0); + g_assert(nbytes == frame_len); + + TEST_SNEP_LOG("sent 0x%02X request\n", req->request); + + ret = near_snep_core_read(sockfd[server], 0, 0, NULL, req_get, req_put); + + return ret; +} + +/* + * @brief Utility: Send \p frame_len bytes of the fragment \p data + * to the socket and call near_snep_core_read + * + * @param[in] frag_len + * @param[in] data + * @return near_bool_t returned by near_snep_core_read + * + * @note does not call near_snep_core_read for now, since it can't handle + * frame without SNEP header + */ +static near_bool_t test_snep_read_send_fragment(size_t frag_len, + uint8_t *data) +{ + size_t nbytes; + + nbytes = send(sockfd[client], data, frag_len, 0); + g_assert(nbytes == frag_len); + + /* + * TODO fragment has no SNEP header. snep_core_read will fail: + * ret = near_snep_core_read(sockfd[server], 0, 0, NULL, + * test_snep_dummy_req_get, test_snep_dummy_req_put); + */ + return TRUE; +} + +/* + * @brief Utility: Receive remaining fragments and store in \p data_recvd + * + * @param[in] frag_len + * @param[in] remaining_bytes + * @param[out] data Must be preallocated + */ +static void test_snep_read_recv_fragments(uint32_t frag_len, + uint32_t remaining_bytes, void *data_recvd) +{ + struct p2p_snep_resp_frame *resp; + uint32_t offset = 0; + int nbytes; + + g_assert(data_recvd); + + resp = g_try_malloc0(frag_len); + g_assert(resp != NULL); + + do { + memset(resp, 0, frag_len); + + /* receive remaining fragments */ + nbytes = recv(sockfd[client], resp, frag_len, 0); + g_assert(nbytes > 0); /* TODO use explicit value? */ + + /* store received data (no header this time) */ + memcpy(data_recvd + offset, resp, nbytes); + offset += nbytes; + } while (offset < remaining_bytes); + + g_free(resp); +} + +/* + * @brief Utility: Confirm that server didn't send any response + */ +static void test_snep_read_no_response(void) +{ + struct p2p_snep_resp_frame *resp; + int nbytes; + + resp = g_try_malloc0(sizeof(*resp)); + g_assert(resp != NULL); + + nbytes = recv(sockfd[client], resp, sizeof(*resp), MSG_DONTWAIT); + g_assert(nbytes < 0); + g_assert(errno == EAGAIN); + + g_free(resp); +} + +/* + * @brief Utility: Verify response sent by the server + * + * @param[in] exp_resp_code Expected response code + * @param[in] exp_resp_info_len Expected response info length + * @param[in] exp_resp_info Expected response info + */ +static void test_snep_read_verify_resp(int exp_resp_code, + uint32_t exp_resp_info_len, uint8_t *exp_resp_info) +{ + struct p2p_snep_resp_frame *resp; + size_t nbytes, frame_len; + + frame_len = NEAR_SNEP_RESP_HEADER_LENGTH + exp_resp_info_len; + resp = test_snep_build_resp_frame(frame_len, 0, 0, 0, NULL); + g_assert(resp != NULL); + + nbytes = recv(sockfd[client], resp, frame_len, 0); + g_assert(nbytes == frame_len); + + TEST_SNEP_LOG("received response = 0x%02X, exp = 0x%02X\n", + resp->response, exp_resp_code); + + g_assert(resp->version == NEAR_SNEP_VERSION); + g_assert(resp->response == exp_resp_code); + g_assert(resp->length == GUINT_TO_BE(exp_resp_info_len)); + g_assert(!memcmp(resp->info, exp_resp_info, exp_resp_info_len)); + + g_free(resp); +} + +/* + * @brief Utility: Verify code of the response sent by the server + * + * @param[in] exp_resp_code Expected response code + */ +static void test_snep_read_verify_resp_code(int exp_resp_code) +{ + test_snep_read_verify_resp(exp_resp_code, 0, NULL); +} + +/* + * @brief Test: Confirm that server is able to handle PUT request + * + * Steps: + * - Send well-formed PUT request + * - Verify server responded with SUCCESS + */ +static void test_snep_read_put_req_ok(gpointer context, gconstpointer gp) +{ + struct test_snep_context *ctx = context; + struct p2p_snep_req_frame *req; + uint32_t frame_len, payload_len; + near_bool_t ret; + + payload_len = ctx->req_info_len; + frame_len = NEAR_SNEP_REQ_PUT_HEADER_LENGTH + payload_len; + + req = test_snep_build_req_frame(frame_len, NEAR_SNEP_VERSION, + NEAR_SNEP_REQ_PUT, ctx->req_info_len, + ctx->req_info, payload_len); + + ret = test_snep_read_req_common(req, frame_len, test_snep_dummy_req_get, + test_snep_dummy_req_put); + g_assert(ret); + + test_snep_read_verify_resp_code(NEAR_SNEP_RESP_SUCCESS); + + g_free(req); +} + +/* + * @brief Test: Confirm that server checks the version field of the request. + * + * Steps: + * - Send PUT request with incorrect version field + * - Verify server responded with UNSUPPORTED VERSION + */ +static void test_snep_read_put_req_unsupp_ver(gpointer context, + gconstpointer gp) +{ + struct test_snep_context *ctx = context; + struct p2p_snep_req_frame *req; + uint32_t frame_len, payload_len; + near_bool_t ret; + + payload_len = ctx->req_info_len; + frame_len = NEAR_SNEP_REQ_PUT_HEADER_LENGTH + payload_len; + + req = test_snep_build_req_frame(frame_len, 0xF8, NEAR_SNEP_REQ_PUT, + ctx->req_info_len, ctx->req_info, payload_len); + + ret = test_snep_read_req_common(req, frame_len, test_snep_dummy_req_get, + test_snep_dummy_req_put); + g_assert(ret); + + test_snep_read_verify_resp_code(NEAR_SNEP_RESP_VERSION); + + g_free(req); +} + +/* + * @brief Test: Confirm that server responds about no support for the + * functionality in request message + * + * Steps: + * - Send PUT request + * - Pass NULL PUT request handler to the near_snep_core_read + * - Verify server responded with NOT IMPLEMENTED + */ +static void test_snep_read_put_req_not_impl(gpointer context, + gconstpointer gp) +{ + struct test_snep_context *ctx = context; + struct p2p_snep_req_frame *req; + uint32_t frame_len, payload_len; + near_bool_t ret; + + payload_len = ctx->req_info_len; + frame_len = NEAR_SNEP_REQ_PUT_HEADER_LENGTH + payload_len; + + req = test_snep_build_req_frame(frame_len, NEAR_SNEP_VERSION, + NEAR_SNEP_REQ_PUT, ctx->req_info_len, + ctx->req_info, payload_len); + + ret = test_snep_read_req_common(req, frame_len, test_snep_dummy_req_get, + NULL); + g_assert(ret); + + test_snep_read_verify_resp_code(NEAR_SNEP_RESP_NOT_IMPL); + + g_free(req); +} + +/* + * @brief Test: Confirm that server is able to receive fragmented request msg + * + * Steps: + * - Send PUT request with incomplete data + * - Verify server responded with CONTINUE + * - Send second fragment of the message + * - Verify server didn't respond + * - Send last fragment of the message + * - Verify server responded with SUCCESS + */ +static void test_snep_read_put_req_fragmented(gpointer context, + gconstpointer gp) +{ + struct test_snep_context *ctx = context; + struct p2p_snep_req_frame *req; + uint32_t frame_len, payload_len; + near_bool_t ret; + + payload_len = ctx->req_info_len / 3; + frame_len = NEAR_SNEP_REQ_PUT_HEADER_LENGTH + payload_len; + + req = test_snep_build_req_frame(frame_len, NEAR_SNEP_VERSION, + NEAR_SNEP_REQ_PUT, ctx->req_info_len, + ctx->req_info, payload_len); + + /* send 1st fragment within PUT request */ + ret = test_snep_read_req_common(req, frame_len, test_snep_dummy_req_get, + test_snep_dummy_req_put); + g_assert(ret); + + test_snep_read_verify_resp_code(NEAR_SNEP_RESP_CONTINUE); + + /* send 2nd fragment */ + ret = test_snep_read_send_fragment(payload_len, + ctx->req_info + payload_len); + g_assert(ret); + + /* do not expect a response */ + test_snep_read_no_response(); + + /* send last fragment */ + ret = test_snep_read_send_fragment(ctx->req_info_len - 2 * payload_len, + ctx->req_info + 2 * payload_len); + g_assert(ret); + + /* + * TODO expected SUCCESS response: + * test_snep_read_verify_resp_code(NEAR_SNEP_RESP_SUCCESS); + */ + + TEST_SNEP_LOG("EXPECTED FAIL: fragments are not received by SNEP\n"); + + g_free(req); +} + +/* + * @brief Test: Confirm that server is able to handle GET request + * + * Steps: + * - Send PUT request with some data + * - Send GET request + * - Verify server responded with SUCCESS and correct data + */ +static void test_snep_read_get_req_ok(gpointer context, gconstpointer gp) +{ + struct test_snep_context *ctx = context; + struct p2p_snep_req_frame *req; + uint32_t frame_len, payload_len, info_len; + near_bool_t ret; + + /* send some data to the server */ + test_snep_read_put_req_ok(context, gp); + + info_len = ctx->req_info_len + NEAR_SNEP_ACC_LENGTH_SIZE; + payload_len = ctx->req_info_len; + + frame_len = NEAR_SNEP_REQ_GET_HEADER_LENGTH + payload_len; + + req = test_snep_build_req_get_frame(frame_len, NEAR_SNEP_VERSION, + NEAR_SNEP_REQ_GET, info_len, + ctx->acc_len, ctx->req_info, payload_len); + + ret = test_snep_read_req_common(req, frame_len, test_snep_dummy_req_get, + test_snep_dummy_req_put); + g_assert(ret); + + test_snep_read_verify_resp(NEAR_SNEP_RESP_SUCCESS, ctx->req_info_len, + ctx->req_info); + + g_free(req); +} + +/* + * @brief Test: Confirm that server responds about no support for the + * functionality in request message + * + * Steps: + * - Send PUT request with some data + * - Send GET request + * - Pass NULL GET request handler to the near_snep_core_read + * - Verify server responded with NOT IMPLEMENTED + */ +static void test_snep_read_get_req_not_impl(gpointer context, + gconstpointer gp) +{ + struct test_snep_context *ctx = context; + struct p2p_snep_req_frame *req; + uint32_t frame_len, payload_len; + near_bool_t ret; + + /* send some data to the server */ + test_snep_read_put_req_ok(context, gp); + + payload_len = ctx->req_info_len; + frame_len = NEAR_SNEP_REQ_GET_HEADER_LENGTH + payload_len; + + /* build REQ GET frame */ + req = test_snep_build_req_get_frame(frame_len, NEAR_SNEP_VERSION, + NEAR_SNEP_REQ_GET, ctx->req_info_len, ctx->acc_len, + ctx->req_info, payload_len); + + /* call snep_core_read with NULL req_get handler */ + ret = test_snep_read_req_common(req, frame_len, NULL, + test_snep_dummy_req_put); + g_assert(ret); + + test_snep_read_verify_resp_code(NEAR_SNEP_RESP_NOT_IMPL); + + g_free(req); +} + +/* + * @brief Test: Confirm that server is able to respond with fragmented message + * + * Steps: + * - Send PUT request with some data + * - Send GET request with Acceptable Length less than actual data length + * - Verify that server returned with incomplete data + * - Send CONTINUE or REJECT request (depending on \p client_resp param) + * - If REJECT requested, verify that server didn't respond + * - If CONTINUE requested, receive remaining fragments and verify data + */ +static void test_snep_read_get_req_frags_client_resp(gpointer context, + gconstpointer gp, uint8_t client_resp) +{ + struct test_snep_context *ctx = context; + struct p2p_snep_req_frame *req; + struct p2p_snep_resp_frame *resp; + uint32_t frame_len, payload_len; + near_bool_t ret; + size_t nbytes; + uint8_t *data_recvd; + uint32_t offset; + uint32_t frag_len, info_len; + + /* send some data to the server */ + test_snep_read_put_req_ok(context, gp); + + payload_len = ctx->req_info_len; + frame_len = NEAR_SNEP_REQ_GET_HEADER_LENGTH + payload_len; + + /* force fragmentation */ + ctx->acc_len = 60; + g_assert(ctx->acc_len < ctx->req_info_len); + g_assert(NEAR_SNEP_REQ_MAX_FRAGMENT_LENGTH < ctx->req_info_len); + + /* TODO frag_len should be calculated based on SNEP acc_len */ + TEST_SNEP_LOG("WORKAROUND: SNEP core ignores the acceptable length\n"); + frag_len = NEAR_SNEP_REQ_MAX_FRAGMENT_LENGTH; + + info_len = ctx->req_info_len + NEAR_SNEP_ACC_LENGTH_SIZE; + + req = test_snep_build_req_get_frame(frame_len, NEAR_SNEP_VERSION, + NEAR_SNEP_REQ_GET, info_len, + ctx->acc_len, ctx->req_info, payload_len); + + /* send GET request */ + ret = test_snep_read_req_common(req, frame_len, test_snep_dummy_req_get, + test_snep_dummy_req_put); + g_assert(ret); + g_free(req); + + frame_len = NEAR_SNEP_RESP_HEADER_LENGTH + payload_len; + resp = test_snep_build_resp_frame(frame_len, 0, 0, 0, NULL); + + /* start receiving fragments */ + nbytes = recv(sockfd[client], resp, frame_len, 0); + g_assert(nbytes == frag_len); + g_assert(resp->length == GUINT_TO_BE(ctx->req_info_len)); + g_assert(resp->info != NULL); + + data_recvd = g_try_malloc0(ctx->req_info_len); + g_assert(data_recvd != NULL); + + /* store received info field */ + memcpy(data_recvd, resp->info, nbytes - NEAR_SNEP_RESP_HEADER_LENGTH); + g_free(resp); + + offset = nbytes - NEAR_SNEP_RESP_HEADER_LENGTH; + + /* 1st fragment has been received, so request resp=CONTINUE/REJECT */ + frame_len = NEAR_SNEP_REQ_PUT_HEADER_LENGTH; + req = test_snep_build_req_frame(frame_len, NEAR_SNEP_VERSION, + client_resp, 0, NULL, 0); + + ret = test_snep_read_req_common(req, frame_len, NULL, NULL); + g_free(req); + + if (client_resp == NEAR_SNEP_REQ_REJECT) { + /* + * TODO server shall not send any response: + * g_assert(ret); + * test_snep_read_no_response(); + */ + + TEST_SNEP_LOG("EXPECTED FAIL: REJECT not handled by SNEP\n"); + } else if (client_resp == NEAR_SNEP_REQ_CONTINUE) { + g_assert(ret); + + /* receive remaining fragments */ + test_snep_read_recv_fragments(frag_len, + ctx->req_info_len - offset, + data_recvd + offset); + + /* verify data */ + g_assert(!memcmp(data_recvd, ctx->req_info, + ctx->req_info_len)); + } + + g_free(data_recvd); +} + +/* Refer to the test_snep_read_get_req_frags_client_resp for description */ +static void test_snep_read_get_frags_continue(gpointer context, + gconstpointer gp) +{ + test_snep_read_get_req_frags_client_resp(context, gp, + NEAR_SNEP_REQ_CONTINUE); +} + +/* Refer to the test_snep_read_get_req_frags_client_resp for description */ +static void test_snep_read_get_frags_reject(gpointer context, + gconstpointer gp) +{ + test_snep_read_get_req_frags_client_resp(context, gp, + NEAR_SNEP_REQ_REJECT); +} + +/* + * @brief Test: Confirm that server is able to send simple response + */ +static void test_snep_response_noinfo(gpointer context, gconstpointer gp) +{ + int bytes_recv; + struct p2p_snep_resp_frame resp; + + near_snep_core_response_noinfo(sockfd[client], NEAR_SNEP_RESP_SUCCESS); + + bytes_recv = recv(sockfd[server], &resp, sizeof(resp), 0); + g_assert(bytes_recv == NEAR_SNEP_RESP_HEADER_LENGTH); + g_assert(resp.version == NEAR_SNEP_VERSION); + g_assert(resp.response == NEAR_SNEP_RESP_SUCCESS); + g_assert(resp.length == 0); +} + +/* + * @brief Test: Confirm that server is able to communicate with the client + */ +static void test_snep_response_put_get_ndef(gpointer context, + gconstpointer gp) +{ + size_t nbytes; + + struct p2p_snep_req_frame *req; + struct p2p_snep_resp_frame *resp; + struct near_ndef_message *ndef; + + near_bool_t ret; + uint frame_len; + + ndef = near_ndef_prepare_text_record("UTF-8", "en-US", "neard"); + g_assert(ndef); + g_assert(ndef->data); + g_assert(ndef->length > 0); + + frame_len = NEAR_SNEP_RESP_HEADER_LENGTH + ndef->length; + + req = g_try_malloc0(frame_len); + g_assert(req); + + req->version = 0x10; + req->request = NEAR_SNEP_REQ_PUT; + req->length = GUINT_TO_BE(ndef->length); + memcpy(req->ndef, ndef->data, ndef->length); + + /* Send PUT request with text record */ + nbytes = send(sockfd[server], req, frame_len, 0); + g_assert(nbytes == frame_len); + + /* UUT */ + ret = near_snep_core_read(sockfd[client], 0, 0, NULL, + test_snep_dummy_req_get, test_snep_dummy_req_put); + g_assert(ret != FALSE); + + resp = g_try_malloc0(frame_len); + g_assert(resp); + + /* Get response from server */ + nbytes = recv(sockfd[server], resp, frame_len, 0); + g_assert(nbytes > 0); + g_assert(resp->response == NEAR_SNEP_RESP_SUCCESS); + + /* Send GET request to retrieve a record */ + req->request = NEAR_SNEP_REQ_GET; + req->length = 0; + nbytes = send(sockfd[server], req, NEAR_SNEP_RESP_HEADER_LENGTH, 0); + g_assert(nbytes > 0); + + /* UUT */ + ret = near_snep_core_read(sockfd[client], 0, 0, NULL, + test_snep_dummy_req_get, test_snep_dummy_req_put); + g_assert(ret != FALSE); + + /* Get response and verify */ + nbytes = recv(sockfd[server], resp, frame_len, 0); + g_assert(nbytes > 0); + g_assert(resp->response == NEAR_SNEP_RESP_SUCCESS); + g_assert(resp->length == GUINT_TO_BE(ndef->length)); + g_assert(!memcmp(resp->info, text, ndef->length)); + + g_free(req); + g_free(resp); + g_free(ndef->data); + g_free(ndef); +} + +int main(int argc, char **argv) +{ + GTestSuite *ts; + GTestFixtureFunc init = test_snep_init; + GTestFixtureFunc exit = test_snep_cleanup; + size_t fs = sizeof(struct test_snep_context); + + g_test_init(&argc, &argv, NULL); + + ts = g_test_create_suite("testSNEP-response"); + g_test_suite_add(ts, + g_test_create_case("noinfo", fs, short_text, + init, test_snep_response_noinfo, exit)); + + g_test_suite_add_suite(g_test_get_root(), ts); + + ts = g_test_create_suite("testSNEP-readGET"); + g_test_suite_add(ts, + g_test_create_case("Request ok", fs, short_text, + init, test_snep_read_get_req_ok, exit)); + g_test_suite_add(ts, + g_test_create_case("Request not implemented", fs, short_text, + init, test_snep_read_get_req_not_impl, exit)); + g_test_suite_add(ts, + g_test_create_case("Request fragmented CONTINUE", + fs, long_text, init, + test_snep_read_get_frags_continue, exit)); + g_test_suite_add(ts, + g_test_create_case("Request fragmented REJECT", + fs, long_text, init, + test_snep_read_get_frags_reject, exit)); + + g_test_suite_add_suite(g_test_get_root(), ts); + + ts = g_test_create_suite("testSNEP-readPUT"); + g_test_suite_add(ts, + g_test_create_case("Request ok", fs, short_text, + init, test_snep_read_put_req_ok, exit)); + g_test_suite_add(ts, + g_test_create_case("Request unsupported ver", fs, short_text, + init, test_snep_read_put_req_unsupp_ver, exit)); + g_test_suite_add(ts, + g_test_create_case("Request not implemented", fs, short_text, + init, test_snep_read_put_req_not_impl, exit)); + g_test_suite_add(ts, + g_test_create_case("Request fragmented", fs, long_text, + init, test_snep_read_put_req_fragmented, exit)); + + g_test_suite_add_suite(g_test_get_root(), ts); + + ts = g_test_create_suite("testSNEP-misc"); + g_test_suite_add(ts, + g_test_create_case("PUT and GET request NDEF", + fs, short_text, init, + test_snep_response_put_get_ndef, exit)); + + g_test_suite_add_suite(g_test_get_root(), ts); + + return g_test_run(); +} diff --git a/unit/test-utils.c b/unit/test-utils.c new file mode 100644 index 0000000..fe25656 --- /dev/null +++ b/unit/test-utils.c @@ -0,0 +1,40 @@ +/* + * neard - Near Field Communication manager + * + * 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 "test-utils.h" + +void test_ndef_free_record(struct near_ndef_record *record) +{ + g_free(record->header); + g_free(record->type); + g_free(record->data); + g_free(record); +} + +struct near_ndef_message *test_ndef_create_test_record(const char *str) +{ + struct near_ndef_message *ndef; + + ndef = near_ndef_prepare_text_record("UTF-8", "en-US", (char *) str); + g_assert(ndef); + g_assert(ndef->data); + + return ndef; +} diff --git a/unit/test-utils.h b/unit/test-utils.h new file mode 100644 index 0000000..c371d56 --- /dev/null +++ b/unit/test-utils.h @@ -0,0 +1,150 @@ +/* + * neard - Near Field Communication manager + * + * 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 + * + */ + +#ifndef UNIT_TEST_UTILS_H +#define UNIT_TEST_UTILS_H + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#include <src/near.h> +#include <near/nfc_copy.h> +#include <near/types.h> +#include <near/ndef.h> +#include <glib.h> +#include <glib/gprintf.h> + +/* SNEP specific types */ +struct snep_fragment { + uint32_t len; + uint8_t *data; +}; + +struct p2p_snep_put_req_data { + uint8_t fd; + uint32_t adapter_idx; + uint32_t target_idx; + near_device_io_cb cb; + guint watch; + + GSList *fragments; +}; + +struct p2p_snep_req_frame { + uint8_t version; + uint8_t request; + uint32_t length; + uint8_t ndef[]; +} __attribute__((packed)); + +struct p2p_snep_resp_frame { + uint8_t version; + uint8_t response; + uint32_t length; + uint8_t info[]; +} __attribute__((packed)); + +/* NDEF specific types */ +enum record_type { + RECORD_TYPE_WKT_SMART_POSTER = 0x01, + RECORD_TYPE_WKT_URI = 0x02, + RECORD_TYPE_WKT_TEXT = 0x03, + RECORD_TYPE_WKT_SIZE = 0x04, + RECORD_TYPE_WKT_TYPE = 0x05, + RECORD_TYPE_WKT_ACTION = 0x06, + RECORD_TYPE_WKT_HANDOVER_REQUEST = 0x07, + RECORD_TYPE_WKT_HANDOVER_SELECT = 0x08, + RECORD_TYPE_WKT_HANDOVER_CARRIER = 0x09, + RECORD_TYPE_WKT_ALTERNATIVE_CARRIER = 0x0a, + RECORD_TYPE_WKT_COLLISION_RESOLUTION = 0x0b, + RECORD_TYPE_WKT_ERROR = 0x0c, + RECORD_TYPE_MIME_TYPE = 0x0d, + RECORD_TYPE_UNKNOWN = 0xfe, + RECORD_TYPE_ERROR = 0xff +}; + +struct near_ndef_record_header { + uint8_t mb; + uint8_t me; + uint8_t cf; + uint8_t sr; + uint8_t il; + uint8_t tnf; + uint8_t il_length; + uint8_t *il_field; + uint32_t payload_len; + uint32_t offset; + uint8_t type_len; + enum record_type rec_type; + char *type_name; + uint32_t header_len; +}; + +struct near_ndef_text_payload { + char *encoding; + char *language_code; + char *data; +}; + +struct near_ndef_uri_payload { + uint8_t identifier; + + uint32_t field_length; + uint8_t *field; +}; + +struct near_ndef_sp_payload { + struct near_ndef_uri_payload *uri; + + uint8_t number_of_title_records; + struct near_ndef_text_payload **title_records; + + uint32_t size; /* from Size record*/ + char *type; /* from Type record*/ + char *action; +}; + +struct near_ndef_record { + char *path; + + struct near_ndef_record_header *header; + + /* specific payloads */ + struct near_ndef_text_payload *text; + struct near_ndef_uri_payload *uri; + struct near_ndef_sp_payload *sp; + struct near_ndef_mime_payload *mime; + struct near_ndef_ho_payload *ho; /* handover payload */ + + char *type; + + uint8_t *data; + size_t data_len; +}; + +void test_ndef_free_record(struct near_ndef_record *record); + +struct near_ndef_message *test_ndef_create_test_record(const char *str); + +#endif |