blob: 22619380c681f6f08e44bb7a160eee114f71b19c [file] [log] [blame] [edit]
/*
*
* neard - Near Field Communication manager
*
* Copyright (C) 2012 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
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdint.h>
#include <errno.h>
#include <stdbool.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/socket.h>
#include <near/nfc_copy.h>
#include <near/plugin.h>
#include <near/log.h>
#include <near/types.h>
#include <near/adapter.h>
#include <near/device.h>
#include <near/ndef.h>
#include <near/tlv.h>
#include <near/snep.h>
#include "p2p.h"
/* Would store incoming ndefs per client */
static GHashTable *snep_validation_hash = NULL;
/* Callback: free validation data */
static void free_snep_validation_client(gpointer data)
{
GList *old_ndefs = data;
if (old_ndefs)
g_list_free(old_ndefs);
}
/* Validation Server REQ_PUT function
* The validation server shall accept PUT and GET requests. A PUT request shall
* cause the server to store the ndef message transmitted with the request.
* */
static bool snep_validation_server_req_put(int client_fd, void *data)
{
struct p2p_snep_data *snep_data = data;
GList *records;
struct near_ndef_record *recd;
GList *incoming_ndefs;
DBG("");
if (!snep_data->nfc_data)
goto error;
/*
* We received a ndef, parse it, check if there's only
* 1 record (a mime type !) with an ID
*/
records = near_ndef_parse_msg(snep_data->nfc_data,
snep_data->nfc_data_length, NULL);
if (g_list_length(records) != 1) {
DBG("records number mismatch");
goto error;
}
recd = records->data;
if (!recd) {
g_list_free(records);
goto error;
}
/* Save the record but look if there are some incoming ndef stored */
incoming_ndefs = g_hash_table_lookup(snep_validation_hash,
GINT_TO_POINTER(client_fd));
incoming_ndefs = g_list_append(incoming_ndefs, recd);
/* remove existing one silently */
g_hash_table_steal(snep_validation_hash, GINT_TO_POINTER(client_fd));
/* push the new one */
g_hash_table_insert(snep_validation_hash, GINT_TO_POINTER(client_fd),
incoming_ndefs);
near_snep_core_response_noinfo(client_fd, NEAR_SNEP_RESP_SUCCESS);
return true;
error:
near_snep_core_response_noinfo(client_fd, NEAR_SNEP_RESP_REJECT);
return true;
}
/*
* Validation Server REQ_GET function
* The validation server shall accept PUT and GET requests. A GET request shall
* cause the server to return a previously stored NDEF message of the same NDEF
* message type and identifier as transmitted with the request.
* */
static bool snep_validation_server_req_get(int client_fd, void *data)
{
struct p2p_snep_data *snep_data = data;
struct near_ndef_record *recd, *rec_store;
uint32_t acceptable_length;
GList *records;
GList *iter;
GList *incoming_ndefs;
DBG("");
/*
* We received a ndef, parse it, check if there's only
* 1 record (a mime type !) with an ID
*/
records = near_ndef_parse_msg(snep_data->nfc_data +
NEAR_SNEP_ACC_LENGTH_SIZE,
snep_data->nfc_data_length - NEAR_SNEP_ACC_LENGTH_SIZE,
NULL);
if (g_list_length(records) != 1) {
DBG("records number mismatch");
goto error;
}
recd = records->data;
if (!recd) {
g_list_free(records);
goto error;
}
/* check if the acceptable length is higher than the data_len
* otherwise returns a NEAR_SNEP_RESP_EXCESS
*/
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,
GINT_TO_POINTER(client_fd));
if (!incoming_ndefs)
goto done;
/* Now, loop to find the the associated record */
for (iter = incoming_ndefs; iter; iter = iter->next) {
rec_store = iter->data;
/* Same mime type and same id ?*/
if (!near_ndef_record_cmp_id(recd, rec_store))
continue;
if (!near_ndef_record_cmp_mime(recd, rec_store))
continue;
/* Found a record, check the length */
if (acceptable_length >= near_ndef_data_length(rec_store)) {
near_snep_core_response_with_info(client_fd,
NEAR_SNEP_RESP_SUCCESS,
near_ndef_data_ptr(rec_store),
near_ndef_data_length(rec_store));
incoming_ndefs = g_list_remove(incoming_ndefs,
iter->data);
/* remove existing one silently */
g_hash_table_steal(snep_validation_hash,
GINT_TO_POINTER(client_fd));
/* push the new one */
g_hash_table_insert(snep_validation_hash,
GINT_TO_POINTER(client_fd),
incoming_ndefs);
} else
near_snep_core_response_noinfo(client_fd, NEAR_SNEP_RESP_EXCESS);
return true;
}
done:
/* If not found */
near_snep_core_response_noinfo(client_fd, NEAR_SNEP_RESP_NOT_FOUND);
return true;
error:
/* Not found */
near_snep_core_response_noinfo(client_fd, NEAR_SNEP_RESP_REJECT);
return false;
}
/* This function is a wrapper to push post processing read functions */
static bool snep_validation_read(int client_fd, uint32_t adapter_idx,
uint32_t target_idx,
near_tag_io_cb cb,
gpointer data)
{
DBG("");
return near_snep_core_read(client_fd, adapter_idx, target_idx, cb,
snep_validation_server_req_get,
snep_validation_server_req_put,
data);
}
static void snep_validation_close(int client_fd, int err, gpointer data)
{
DBG("");
g_hash_table_remove(snep_validation_hash, GINT_TO_POINTER(client_fd));
/* Call core server close */
near_snep_core_close(client_fd, err, data);
}
struct near_p2p_driver validation_snep_driver = {
.name = "VALIDATION_SNEP",
.service_name = "urn:nfc:xsn:nfc-forum.org:snep-validation",
.fallback_service_name = NULL,
.sock_type = SOCK_STREAM,
.read = snep_validation_read,
.push = near_snep_core_push,
.close = snep_validation_close,
};
int snep_validation_init(void)
{
/* Would store incoming ndefs per client */
snep_validation_hash = g_hash_table_new_full(g_direct_hash,
g_direct_equal, NULL,
free_snep_validation_client);
return near_p2p_register(&validation_snep_driver);
}
void snep_validation_exit(void)
{
near_p2p_unregister(&validation_snep_driver);
g_hash_table_destroy(snep_validation_hash);
snep_validation_hash = NULL;
}