spider-bot/fw/nrf52/nrf5_sdk/components/iot/lwm2m/lwm2m.c

886 lines
26 KiB
C

/**
* Copyright (c) 2015 - 2019, Nordic Semiconductor ASA
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form, except as embedded into a Nordic
* Semiconductor ASA integrated circuit in a product or a software update for
* such product, must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 4. This software, with or without modification, must only be used with a
* Nordic Semiconductor ASA integrated circuit.
*
* 5. Any software provided in binary form under this license must not be reverse
* engineered, decompiled, modified and/or disassembled.
*
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "lwm2m_api.h"
#include "lwm2m_register.h"
#include "lwm2m_bootstrap.h"
#include "sdk_os.h"
#include "lwm2m.h"
#include "sdk_config.h"
#if LWM2M_CONFIG_LOG_ENABLED
#define NRF_LOG_MODULE_NAME lwm2m
#define NRF_LOG_LEVEL LWM2M_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR LWM2M_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR LWM2M_CONFIG_DEBUG_COLOR
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define LWM2M_TRC NRF_LOG_DEBUG /**< Used for getting trace of execution in the module. */
#define LWM2M_ERR NRF_LOG_ERROR /**< Used for logging errors in the module. */
#define LWM2M_DUMP NRF_LOG_HEXDUMP_DEBUG /**< Used for dumping octet information to get details of bond information etc. */
#define LWM2M_ENTRY() LWM2M_TRC(">> %s", __func__)
#define LWM2M_EXIT() LWM2M_TRC("<< %s", __func__)
#else // LWM2M_CONFIG_LOG_ENABLED
#define LWM2M_TRC(...) /**< Disables traces. */
#define LWM2M_DUMP(...) /**< Disables dumping of octet streams. */
#define LWM2M_ERR(...) /**< Disables error logs. */
#define LWM2M_ENTRY(...)
#define LWM2M_EXIT(...)
#endif // LWM2M_CONFIG_LOG_ENABLED
#if (LWM2M_CONFIG_LOG_ENABLED != 0)
static uint8_t op_desc_idx_lookup(uint8_t bitmask)
{
for (uint8_t i = 0; i < 8; i++)
{
if ((bitmask > i) == 0x1)
{
return i;
}
}
// If no bits where set in the bitmask.
return 0;
}
static const char m_operation_desc[8][9] = {
"NONE",
"READ",
"WRITE",
"EXECUTE",
"DELETE",
"CREATE",
"DISCOVER",
"OBSERVE"
};
#endif
SDK_MUTEX_DEFINE(m_lwm2m_mutex) /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */
static lwm2m_object_prototype_t * m_objects[LWM2M_COAP_HANDLER_MAX_OBJECTS];
static lwm2m_instance_prototype_t * m_instances[LWM2M_COAP_HANDLER_MAX_INSTANCES];
static uint16_t m_num_objects;
static uint16_t m_num_instances;
static void coap_error_handler(uint32_t error_code, coap_message_t * p_message)
{
LWM2M_ERR("[CoAP]: Unhandled coap message recieved. Error code: %lu", error_code);
}
static void internal_coap_handler_init(void)
{
memset(m_objects, 0, sizeof(m_objects));
memset(m_instances, 0, sizeof(m_instances));
m_num_objects = 0;
m_num_instances = 0;
}
static bool numbers_only(const char * p_str, uint16_t str_len)
{
for (uint16_t i = 0; i < str_len; i++)
{
if (isdigit(p_str[i]) == 0)
{
return false;
}
}
return true;
}
static uint32_t instance_resolve(lwm2m_instance_prototype_t ** p_instance,
uint16_t object_id,
uint16_t instance_id)
{
for (int i = 0; i < m_num_instances; ++i)
{
if (m_instances[i]->object_id == object_id &&
m_instances[i]->instance_id == instance_id)
{
if (m_instances[i]->callback == NULL)
{
return NRF_ERROR_NULL;
}
*p_instance = m_instances[i];
return NRF_SUCCESS;
}
}
return NRF_ERROR_NOT_FOUND;
}
static uint32_t object_resolve(lwm2m_object_prototype_t ** p_instance,
uint16_t object_id)
{
for (int i = 0; i < m_num_objects; ++i)
{
if (m_objects[i]->object_id == object_id)
{
if (m_objects[i]->callback == NULL)
{
return NRF_ERROR_NULL;
}
*p_instance = m_objects[i];
return NRF_SUCCESS;
}
}
return NRF_ERROR_NOT_FOUND;
}
static uint32_t op_code_resolve(lwm2m_instance_prototype_t * p_instance,
uint16_t resource_id,
uint8_t * operation)
{
uint8_t * operations = (uint8_t *) p_instance + p_instance->operations_offset;
uint16_t * operations_ids = (uint16_t *)((uint8_t *) p_instance +
p_instance->resource_ids_offset);
for (int j = 0; j < p_instance->num_resources; ++j)
{
if (operations_ids[j] == resource_id)
{
*operation = operations[j];
return NRF_SUCCESS;
}
}
return NRF_ERROR_NOT_FOUND;
}
static uint32_t internal_request_handle(coap_message_t * p_request,
uint16_t * p_path,
uint8_t path_len)
{
uint32_t err_code;
uint8_t operation = LWM2M_OPERATION_CODE_NONE;
uint32_t content_type = 0;
err_code = coap_message_ct_mask_get(p_request, &content_type);
if (err_code != NRF_SUCCESS)
{
return err_code;
}
/**
* TODO: the methods should check if we have read / write / execute rights
* through ACL and resource operations
*/
switch (p_request->header.code)
{
case COAP_CODE_GET:
{
LWM2M_TRC("[CoAP]: CoAP GET request");
if (content_type == COAP_CT_APP_LINK_FORMAT) // Discover
{
operation = LWM2M_OPERATION_CODE_DISCOVER;
}
else // Read
{
operation = LWM2M_OPERATION_CODE_READ;
}
break;
}
case COAP_CODE_PUT:
{
operation = LWM2M_OPERATION_CODE_WRITE;
break;
}
case COAP_CODE_POST:
{
operation = LWM2M_OPERATION_CODE_WRITE;
break;
}
case COAP_CODE_DELETE:
{
operation = LWM2M_OPERATION_CODE_DELETE;
break;
}
default:
break; // Maybe send response with unsupported method not allowed?
}
err_code = NRF_ERROR_NOT_FOUND;
switch (path_len)
{
case 0:
{
if (operation == LWM2M_OPERATION_CODE_DELETE)
{
LWM2M_TRC("[CoAP]: >> %s root /",
m_operation_desc[op_desc_idx_lookup(operation)]);
LWM2M_MUTEX_UNLOCK();
err_code = lwm2m_coap_handler_root(LWM2M_OPERATION_CODE_DELETE, p_request);
LWM2M_MUTEX_LOCK();
LWM2M_TRC("[CoAP]: << %s root /",
m_operation_desc[op_desc_idx_lookup(operation)]);
}
break;
}
case 1:
{
LWM2M_TRC("[CoAP]: >> %s object /%u/",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0]);
lwm2m_object_prototype_t * p_object;
err_code = object_resolve(&p_object, p_path[0]);
if (err_code != NRF_SUCCESS)
{
break;
}
LWM2M_MUTEX_UNLOCK();
err_code = p_object->callback(p_object, LWM2M_INVALID_INSTANCE, operation, p_request);
LWM2M_MUTEX_LOCK();
LWM2M_TRC("[CoAP]: << %s object /%u/, result: %s",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0],
(err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
break;
}
case 2:
{
LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0],
p_path[1]);
lwm2m_instance_prototype_t * p_instance;
err_code = instance_resolve(&p_instance, p_path[0], p_path[1]);
if (err_code == NRF_SUCCESS)
{
LWM2M_MUTEX_UNLOCK();
err_code = p_instance->callback(p_instance, LWM2M_INVALID_RESOURCE, operation, p_request);
LWM2M_MUTEX_LOCK();
LWM2M_TRC("[CoAP]: << %s instance /%u/%u/, result: %s",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0],
p_path[1],
(err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
break;
}
// Bootstrap can write to non-existing instances
if (err_code == NRF_ERROR_NOT_FOUND &&
operation == LWM2M_OPERATION_CODE_WRITE &&
p_request->header.code == COAP_CODE_PUT)
{
LWM2M_TRC("[CoAP]: >> %s object /%u/%u/",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0],
p_path[1]);
lwm2m_object_prototype_t * p_object;
err_code = object_resolve(&p_object, p_path[0]);
if (err_code != NRF_SUCCESS)
{
break;
}
LWM2M_MUTEX_UNLOCK();
err_code = p_object->callback(p_object, p_path[1], operation, p_request);
LWM2M_MUTEX_LOCK();
LWM2M_TRC("[CoAP]: << %s object /%u/%u/, result: %s",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0],
p_path[1],
(err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
}
if (err_code == NRF_ERROR_NOT_FOUND &&
operation == LWM2M_OPERATION_CODE_WRITE &&
p_request->header.code == COAP_CODE_POST)
{
LWM2M_TRC("[CoAP]: >> CREATE object /%u/%u/",
p_path[0],
p_path[1]);
lwm2m_object_prototype_t * p_object;
err_code = object_resolve(&p_object, p_path[0]);
if (err_code != NRF_SUCCESS)
{
break;
}
LWM2M_MUTEX_UNLOCK();
err_code = p_object->callback(p_object, p_path[1], LWM2M_OPERATION_CODE_CREATE, p_request);
LWM2M_MUTEX_LOCK();
LWM2M_TRC("[CoAP]: << CREATE object /%u/%u/, result: %s",
p_path[0],
p_path[1],
(err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
break;
}
break;
}
case 3:
{
if (operation == LWM2M_OPERATION_CODE_DELETE)
{
// Deleting resources within an instance not allowed.
break;
}
if (p_request->header.code == COAP_CODE_POST)
{
for (int i = 0; i < m_num_instances; ++i)
{
if ((m_instances[i]->object_id == p_path[0]) &&
(m_instances[i]->instance_id == p_path[1]))
{
if (m_instances[i]->callback == NULL)
{
err_code = NRF_ERROR_NULL;
break;
}
uint8_t resource_operation = 0;
err_code = op_code_resolve(m_instances[i], p_path[2], &resource_operation);
if (err_code != NRF_SUCCESS)
break;
if ((resource_operation & LWM2M_OPERATION_CODE_EXECUTE) > 0)
{
operation = LWM2M_OPERATION_CODE_EXECUTE;
}
if ((resource_operation & LWM2M_OPERATION_CODE_WRITE) > 0)
{
operation = LWM2M_OPERATION_CODE_WRITE;
}
LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/%u/",
m_operation_desc[op_desc_idx_lookup(operation)],
m_instances[i]->object_id,
m_instances[i]->instance_id,
p_path[2]);
LWM2M_MUTEX_UNLOCK();
(void)m_instances[i]->callback(m_instances[i],
p_path[2],
operation,
p_request);
LWM2M_MUTEX_LOCK();
err_code = NRF_SUCCESS;
LWM2M_TRC("[CoAP]: << %s instance /%u/%u/%u/",
m_operation_desc[op_desc_idx_lookup(operation)],
m_instances[i]->object_id,
m_instances[i]->instance_id,
p_path[2]);
break;
}
}
}
else
{
LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/%u/",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0],
p_path[1],
p_path[2]);
lwm2m_instance_prototype_t * p_instance;
err_code = instance_resolve(&p_instance, p_path[0], p_path[1]);
if (err_code != NRF_SUCCESS)
{
break;
}
LWM2M_MUTEX_UNLOCK();
err_code = p_instance->callback(p_instance, p_path[2], operation, p_request);
LWM2M_MUTEX_LOCK();
LWM2M_TRC("[CoAP]: << %s instance /%u/%u/%u/, result: %s",
m_operation_desc[op_desc_idx_lookup(operation)],
p_path[0],
p_path[1],
p_path[2],
(err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
}
break;
}
default:
break;
}
return err_code;
}
static uint32_t lwm2m_coap_handler_handle_request(coap_message_t * p_request)
{
LWM2M_ENTRY();
uint16_t index;
uint16_t path[3];
char * endptr;
bool is_numbers_only = true;
uint16_t path_index = 0;
uint32_t err_code = NRF_SUCCESS;
LWM2M_MUTEX_LOCK();
for (index = 0; index < p_request->options_count; index++)
{
if (p_request->options[index].number == COAP_OPT_URI_PATH)
{
uint16_t option_len = p_request->options[index].length;
bool numbers = numbers_only((char *)p_request->options[index].p_data,
option_len);
if (numbers)
{
// Declare a temporary array that is 1 byte longer than the
// option data in order to leave space for a terminating character.
uint8_t option_data[option_len + 1];
// Set the temporary array to zero.
memset(option_data, 0, sizeof(option_data));
// Copy the option data string to the temporary array.
memcpy(option_data, p_request->options[index].p_data, option_len);
// Convert the zero-terminated string to a long int value.
path[path_index] = strtol((char *)option_data, &endptr, 10);
++path_index;
if (endptr == ((char *)option_data))
{
err_code = NRF_ERROR_NOT_FOUND;
break;
}
if (endptr != ((char *)option_data + option_len))
{
err_code = NRF_ERROR_NOT_FOUND;
break;
}
}
else
{
is_numbers_only = false;
break;
}
}
}
if (err_code == NRF_SUCCESS)
{
if (is_numbers_only == true)
{
err_code = internal_request_handle(p_request, path, path_index);
}
else
{
// If uri path did not consist of numbers only.
char * requested_uri = NULL;
for (index = 0; index < p_request->options_count; index++)
{
if (p_request->options[index].number == COAP_OPT_URI_PATH)
{
requested_uri = (char *)p_request->options[index].p_data;
// Stop on first URI hit.
break;
}
}
if (requested_uri == NULL)
{
err_code = NRF_ERROR_NOT_FOUND;
}
else
{
// Try to look up if there is a match with object with an alias name.
for (int i = 0; i < m_num_objects; ++i)
{
if (m_objects[i]->object_id == LWM2M_NAMED_OBJECT)
{
size_t size = strlen(m_objects[i]->p_alias_name);
if ((strncmp(m_objects[i]->p_alias_name, requested_uri, size) == 0))
{
if (m_objects[i]->callback == NULL)
{
err_code = NRF_ERROR_NULL;
break;
}
LWM2M_MUTEX_UNLOCK();
err_code = m_objects[i]->callback(m_objects[i],
LWM2M_INVALID_INSTANCE,
LWM2M_OPERATION_CODE_NONE,
p_request);
LWM2M_MUTEX_LOCK();
break;
}
}
else
{
// This is not a name object, return error code.
err_code = NRF_ERROR_NOT_FOUND;
break;
}
}
}
}
}
LWM2M_MUTEX_UNLOCK();
LWM2M_EXIT();
return err_code;
}
uint32_t lwm2m_coap_handler_instance_add(lwm2m_instance_prototype_t * p_instance)
{
LWM2M_ENTRY();
NULL_PARAM_CHECK(p_instance);
LWM2M_MUTEX_LOCK();
if (m_num_instances == LWM2M_COAP_HANDLER_MAX_INSTANCES)
{
LWM2M_MUTEX_UNLOCK();
return NRF_ERROR_NO_MEM;
}
m_instances[m_num_instances] = p_instance;
++m_num_instances;
LWM2M_MUTEX_UNLOCK();
return NRF_SUCCESS;
}
uint32_t lwm2m_coap_handler_instance_delete(lwm2m_instance_prototype_t * p_instance)
{
LWM2M_ENTRY();
NULL_PARAM_CHECK(p_instance);
LWM2M_MUTEX_LOCK();
for (int i = 0; i < m_num_instances; ++i)
{
if ((m_instances[i]->object_id == p_instance->object_id) &&
(m_instances[i]->instance_id == p_instance->instance_id))
{
// Move current last entry into this index position, and trim down the length.
// If this is the last element, it cannot be accessed because the m_num_instances
// count is 0.
m_instances[i] = m_instances[m_num_instances - 1];
--m_num_instances;
LWM2M_MUTEX_UNLOCK();
return NRF_SUCCESS;
}
}
LWM2M_MUTEX_UNLOCK();
return NRF_ERROR_NOT_FOUND;
}
uint32_t lwm2m_coap_handler_object_add(lwm2m_object_prototype_t * p_object)
{
LWM2M_ENTRY();
NULL_PARAM_CHECK(p_object);
LWM2M_MUTEX_LOCK();
if (m_num_objects == LWM2M_COAP_HANDLER_MAX_INSTANCES)
{
LWM2M_MUTEX_UNLOCK();
return NRF_ERROR_NO_MEM;
}
m_objects[m_num_objects] = p_object;
++m_num_objects;
LWM2M_MUTEX_UNLOCK();
return NRF_SUCCESS;
}
uint32_t lwm2m_coap_handler_object_delete(lwm2m_object_prototype_t * p_object)
{
LWM2M_ENTRY();
NULL_PARAM_CHECK(p_object);
LWM2M_MUTEX_LOCK();
for (int i = 0; i < m_num_objects; ++i)
{
if ( m_objects[i]->object_id == p_object->object_id)
{
// Move current last entry into this index position, and trim down the length.
// If this is the last element, it cannot be accessed because the m_num_objects
// count is 0.
m_objects[i] = m_objects[m_num_objects - 1];
--m_num_objects;
LWM2M_MUTEX_UNLOCK();
return NRF_SUCCESS;
}
}
LWM2M_MUTEX_UNLOCK();
return NRF_ERROR_NOT_FOUND;
}
uint32_t lwm2m_coap_handler_gen_link_format(uint8_t * p_buffer, uint16_t * p_buffer_len)
{
LWM2M_ENTRY();
NULL_PARAM_CHECK(p_buffer_len);
LWM2M_MUTEX_LOCK();
uint16_t buffer_index = 0;
uint8_t * p_string_buffer;
uint16_t buffer_max_size;
uint8_t dry_run_buffer[16];
bool dry_run = false;
uint16_t dry_run_size = 0;
if (p_buffer == NULL)
{
// Dry-run only, in order to calculate the size of the needed buffer.
dry_run = true;
p_string_buffer = dry_run_buffer;
buffer_max_size = sizeof(dry_run_buffer);
}
else
{
p_string_buffer = p_buffer;
buffer_max_size = *p_buffer_len;
}
for (int i = 0; i < m_num_objects; ++i)
{
// We need more than 3 chars to write a new link
if (buffer_max_size - buffer_index <= 3)
{
LWM2M_MUTEX_UNLOCK();
return NRF_ERROR_NO_MEM;
}
uint16_t curr_object = m_objects[i]->object_id;
if (curr_object == LWM2M_NAMED_OBJECT)
{
// Skip this object as it is a named object.
continue;
}
bool instance_present = false;
for (int j = 0; j < m_num_instances; ++j)
{
if (m_instances[j]->object_id == curr_object)
{
instance_present = true;
buffer_index += snprintf((char *)&p_string_buffer[buffer_index],
buffer_max_size - buffer_index,
"</%u/%u>,",
m_instances[j]->object_id,
m_instances[j]->instance_id);
if (dry_run == true)
{
dry_run_size += buffer_index;
buffer_index = 0;
}
}
}
if (!instance_present)
{
buffer_index += snprintf((char *)&p_string_buffer[buffer_index],
buffer_max_size - buffer_index,
"</%u>,",
curr_object);
if (dry_run == true)
{
dry_run_size += buffer_index;
buffer_index = 0;
}
}
}
if (dry_run == true)
{
*p_buffer_len = dry_run_size - 1;
}
else
{
*p_buffer_len = buffer_index - 1; // Remove the last comma
}
LWM2M_MUTEX_UNLOCK();
return NRF_SUCCESS;
}
uint32_t lwm2m_init(void)
{
SDK_MUTEX_INIT(m_lwm2m_mutex);
LWM2M_MUTEX_LOCK();
uint32_t err_code;
err_code = internal_lwm2m_register_init();
if (err_code != NRF_SUCCESS)
{
LWM2M_MUTEX_UNLOCK();
return err_code;
}
err_code = internal_lwm2m_bootstrap_init();
if (err_code != NRF_SUCCESS)
{
LWM2M_MUTEX_UNLOCK();
return err_code;
}
err_code = coap_error_handler_register(coap_error_handler);
if (err_code != NRF_SUCCESS)
{
LWM2M_MUTEX_UNLOCK();
return err_code;
}
internal_coap_handler_init();
err_code = coap_request_handler_register(lwm2m_coap_handler_handle_request);
LWM2M_MUTEX_UNLOCK();
return err_code;
}