mirror of https://github.com/ARMmbed/mbed-os.git
390 lines
13 KiB
C
390 lines
13 KiB
C
/*
|
|
* Copyright (c) 2015-2016 ARM Limited. All Rights Reserved.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "nsdynmemLIB.h"
|
|
#include "coap_message_handler.h"
|
|
#include "sn_coap_protocol.h"
|
|
#include "ns_types.h"
|
|
#include "ns_list.h"
|
|
#include "ns_trace.h"
|
|
#include "randLIB.h"
|
|
|
|
#define TRACE_GROUP "CoSA"
|
|
|
|
static void *own_alloc(uint16_t size)
|
|
{
|
|
if (size) {
|
|
return ns_dyn_mem_temporary_alloc(size);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void own_free(void *ptr)
|
|
{
|
|
if (ptr) {
|
|
ns_dyn_mem_free(ptr);
|
|
}
|
|
}
|
|
|
|
static NS_LIST_DEFINE(request_list, coap_transaction_t, link);
|
|
|
|
static coap_transaction_t *transaction_find_client_by_token(uint8_t token[4])
|
|
{
|
|
coap_transaction_t *this = NULL;
|
|
ns_list_foreach(coap_transaction_t, cur_ptr, &request_list) {
|
|
if (memcmp(cur_ptr->token,token,4) == 0 && cur_ptr->client_request) {
|
|
this = cur_ptr;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
static coap_transaction_t *transaction_find_server(uint16_t msg_id)
|
|
{
|
|
coap_transaction_t *this = NULL;
|
|
ns_list_foreach(coap_transaction_t, cur_ptr, &request_list) {
|
|
if (cur_ptr->msg_id == msg_id && !cur_ptr->client_request) {
|
|
this = cur_ptr;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
static coap_transaction_t *transaction_find_by_address(uint8_t *address_ptr, uint16_t port)
|
|
{
|
|
coap_transaction_t *this = NULL;
|
|
ns_list_foreach(coap_transaction_t, cur_ptr, &request_list) {
|
|
if (cur_ptr->remote_port == port && memcmp(cur_ptr->remote_address, address_ptr, 16) == 0) {
|
|
this = cur_ptr;
|
|
break;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
static coap_transaction_t *transaction_create(void)
|
|
{
|
|
coap_transaction_t *this = ns_dyn_mem_alloc(sizeof(coap_transaction_t));
|
|
if (this) {
|
|
memset(this, 0, sizeof(coap_transaction_t));
|
|
this->client_request = true;// default to client initiated method
|
|
ns_list_add_to_start(&request_list, this);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
static void transaction_delete(coap_transaction_t *this)
|
|
{
|
|
if (this) {
|
|
ns_list_remove(&request_list, this);
|
|
if(this->data_ptr){
|
|
ns_dyn_mem_free(this->data_ptr);
|
|
}
|
|
ns_dyn_mem_free(this);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int8_t coap_rx_function(sn_coap_hdr_s *resp_ptr, sn_nsdl_addr_s *address_ptr, void *param)
|
|
{
|
|
coap_transaction_t *this = NULL;
|
|
(void)address_ptr;
|
|
(void)param;
|
|
tr_warn("transaction was not handled");
|
|
if (!resp_ptr) {
|
|
return -1;
|
|
}
|
|
if( resp_ptr->token_ptr ){
|
|
this = transaction_find_client_by_token(resp_ptr->token_ptr);
|
|
}
|
|
if (this && this->resp_cb) {
|
|
this->resp_cb(this->service_id, address_ptr->addr_ptr, address_ptr->port, NULL);
|
|
}
|
|
transaction_delete(this);
|
|
return 0;
|
|
}
|
|
|
|
static void coap_service_build_content_format(sn_coap_hdr_s *header, sn_coap_content_format_e format);
|
|
|
|
coap_msg_handler_t *coap_message_handler_init(void *(*used_malloc_func_ptr)(uint16_t), void (*used_free_func_ptr)(void *),
|
|
uint8_t (*used_tx_callback_ptr)(uint8_t *, uint16_t, sn_nsdl_addr_s *, void *)){
|
|
|
|
if ((used_malloc_func_ptr == NULL) || (used_free_func_ptr == NULL) || (used_tx_callback_ptr == NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
coap_msg_handler_t *handle;
|
|
handle = used_malloc_func_ptr(sizeof(coap_msg_handler_t));
|
|
if (handle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(handle, 0, sizeof(coap_msg_handler_t));
|
|
|
|
handle->sn_coap_tx_callback = used_tx_callback_ptr;
|
|
|
|
handle->sn_coap_service_free = used_free_func_ptr;
|
|
handle->sn_coap_service_malloc = used_malloc_func_ptr;
|
|
|
|
handle->coap = sn_coap_protocol_init(used_malloc_func_ptr, used_free_func_ptr, used_tx_callback_ptr, &coap_rx_function);
|
|
if( !handle->coap ){
|
|
used_free_func_ptr(handle);
|
|
return NULL;
|
|
}
|
|
return handle;
|
|
}
|
|
|
|
int8_t coap_message_handler_destroy(coap_msg_handler_t *handle){
|
|
if( !handle ){
|
|
return -1;
|
|
}
|
|
|
|
if( handle->coap ){
|
|
sn_coap_protocol_destroy(handle->coap);
|
|
}
|
|
|
|
//Destroy transactions
|
|
ns_list_foreach_safe(coap_transaction_t, cur_ptr, &request_list) {
|
|
ns_list_remove(&request_list, cur_ptr);
|
|
ns_dyn_mem_free(cur_ptr);
|
|
cur_ptr = NULL;
|
|
}
|
|
|
|
handle->sn_coap_service_free(handle);
|
|
return 0;
|
|
}
|
|
|
|
coap_transaction_t *coap_message_handler_transaction_valid(coap_transaction_t *tr_ptr)
|
|
{
|
|
ns_list_foreach(coap_transaction_t, cur_ptr, &request_list) {
|
|
if (cur_ptr == tr_ptr) {
|
|
return tr_ptr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
coap_transaction_t *coap_message_handler_find_transaction(uint8_t *address_ptr, uint16_t port)
|
|
{
|
|
if( !address_ptr )
|
|
return NULL;
|
|
return transaction_find_by_address( address_ptr, port );
|
|
}
|
|
|
|
int16_t coap_message_handler_coap_msg_process(coap_msg_handler_t *handle, int8_t socket_id, uint8_t source_addr_ptr[static 16], uint16_t port,
|
|
uint8_t *data_ptr, uint16_t data_len, int16_t (cb)(int8_t, sn_coap_hdr_s *, coap_transaction_t *))
|
|
{
|
|
if( !cb || !handle ){
|
|
return -1;
|
|
}
|
|
sn_nsdl_addr_s src_addr;
|
|
sn_coap_hdr_s *coap_message;
|
|
src_addr.addr_ptr = source_addr_ptr;
|
|
src_addr.addr_len = 16;
|
|
src_addr.type = SN_NSDL_ADDRESS_TYPE_IPV6;
|
|
src_addr.port = port;
|
|
|
|
coap_message = sn_coap_protocol_parse(handle->coap, &src_addr, data_len, data_ptr, NULL);
|
|
if (coap_message == NULL) {
|
|
tr_err("CoAP Parsing failed");
|
|
return -1;
|
|
}
|
|
tr_debug("CoAP status:%d, type:%d, code:%d, id:%d", coap_message->coap_status, coap_message->msg_type, coap_message->msg_code, coap_message->msg_id);
|
|
/* Check, if coap itself sends response, or block receiving is ongoing... */
|
|
if (coap_message->coap_status != COAP_STATUS_OK && coap_message->coap_status != COAP_STATUS_PARSER_BLOCKWISE_MSG_RECEIVED) {
|
|
tr_debug("CoAP library responds");
|
|
sn_coap_parser_release_allocated_coap_msg_mem(handle->coap, coap_message);
|
|
return -1;
|
|
}
|
|
if (coap_message->msg_code > 0 && coap_message->msg_code < 32) {
|
|
|
|
//TODO Sorry
|
|
|
|
coap_transaction_t *transaction_ptr = transaction_create();
|
|
if (transaction_ptr) {
|
|
|
|
transaction_ptr->msg_id = coap_message->msg_id;
|
|
transaction_ptr->client_request = false;// this is server transaction
|
|
memcpy(transaction_ptr->remote_address, source_addr_ptr, 16);
|
|
transaction_ptr->remote_port = port;
|
|
|
|
int ret = cb(socket_id, coap_message, transaction_ptr);
|
|
if (ret != 0) {
|
|
tr_debug("Service %d, no response expected", transaction_ptr->service_id);
|
|
sn_coap_parser_release_allocated_coap_msg_mem(handle->coap, coap_message);
|
|
transaction_delete(transaction_ptr);
|
|
return -1;
|
|
}
|
|
}else{
|
|
//TODO: handle error case
|
|
}
|
|
} else {
|
|
//response find by MSG id
|
|
coap_transaction_t *this = NULL;
|
|
if( coap_message->token_ptr ){
|
|
this = transaction_find_client_by_token(coap_message->token_ptr);
|
|
}
|
|
if (!this) {
|
|
tr_error("client transaction not found");
|
|
sn_coap_parser_release_allocated_coap_msg_mem(handle->coap, coap_message);
|
|
return -1;
|
|
}
|
|
tr_debug("Service %d, response received", this->service_id);
|
|
if (this->resp_cb) {
|
|
this->resp_cb(this->service_id, source_addr_ptr, port, coap_message);
|
|
}
|
|
sn_coap_parser_release_allocated_coap_msg_mem(handle->coap, coap_message);
|
|
transaction_delete(this);
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
uint16_t coap_message_handler_request_send(coap_msg_handler_t *handle, int8_t service_id, uint8_t options, const uint8_t destination_addr[static 16],
|
|
uint16_t destination_port, sn_coap_msg_type_e msg_type, sn_coap_msg_code_e msg_code, const char *uri,
|
|
sn_coap_content_format_e cont_type, const uint8_t *payload_ptr, uint16_t payload_len, coap_message_handler_response_recv *request_response_cb)
|
|
{
|
|
coap_transaction_t *transaction_ptr;
|
|
sn_coap_hdr_s request;
|
|
sn_nsdl_addr_s dst_addr;
|
|
uint8_t token[4];
|
|
uint16_t data_len;
|
|
uint8_t *data_ptr;
|
|
|
|
tr_debug("Service %d, send CoAP request payload_len %d", service_id, payload_len);
|
|
transaction_ptr = transaction_create();
|
|
|
|
if (!uri || !transaction_ptr) {
|
|
return 0;
|
|
}
|
|
|
|
transaction_ptr->service_id = service_id;
|
|
transaction_ptr->client_request = true;
|
|
transaction_ptr->resp_cb = request_response_cb;
|
|
transaction_ptr->options = options;
|
|
memcpy(transaction_ptr->remote_address, destination_addr, 16);
|
|
transaction_ptr->remote_port = destination_port;
|
|
memset(&request, 0, sizeof(request));
|
|
dst_addr.addr_ptr = (uint8_t *) destination_addr; // Cast away const and trust that nsdl doesn't modify...
|
|
dst_addr.addr_len = 16;
|
|
dst_addr.type = SN_NSDL_ADDRESS_TYPE_IPV6;
|
|
dst_addr.port = destination_port;
|
|
|
|
request.msg_type = msg_type;
|
|
request.msg_code = msg_code;
|
|
request.uri_path_ptr = (uint8_t *)uri;
|
|
request.uri_path_len = strlen(uri);
|
|
coap_service_build_content_format(&request, cont_type);
|
|
|
|
do{
|
|
randLIB_get_n_bytes_random(token,4);
|
|
}while(transaction_find_client_by_token(token));
|
|
memcpy(transaction_ptr->token,token,4);
|
|
request.token_ptr = transaction_ptr->token;
|
|
request.token_len = 4;
|
|
|
|
request.payload_len = payload_len;
|
|
request.payload_ptr = (uint8_t *) payload_ptr; // Cast away const and trust that nsdl doesn't modify...
|
|
data_len = sn_coap_builder_calc_needed_packet_data_size(&request);
|
|
data_ptr = own_alloc(data_len);
|
|
if(data_len > 0 && !data_ptr){
|
|
transaction_delete(transaction_ptr);
|
|
return 0;
|
|
}
|
|
sn_coap_protocol_build(handle->coap, &dst_addr, data_ptr, &request, transaction_ptr);
|
|
transaction_ptr->msg_id = request.msg_id;
|
|
handle->sn_coap_tx_callback(data_ptr, data_len, &dst_addr, transaction_ptr);
|
|
|
|
// Free allocated data
|
|
own_free(data_ptr);
|
|
if(request_response_cb == NULL){
|
|
//No response expected
|
|
return 0;
|
|
}
|
|
return transaction_ptr->msg_id;
|
|
}
|
|
|
|
//TODO: refactor this to use nsdl
|
|
int8_t coap_message_handler_response_send(coap_msg_handler_t *handle, int8_t service_id, uint8_t options, sn_coap_hdr_s *request_ptr, sn_coap_msg_code_e message_code, sn_coap_content_format_e content_type, const uint8_t *payload_ptr, uint16_t payload_len)
|
|
{
|
|
coap_transaction_t *transaction_ptr;
|
|
sn_coap_hdr_s *response;
|
|
sn_nsdl_addr_s dst_addr;
|
|
uint16_t data_len;
|
|
uint8_t *data_ptr;
|
|
(void) options;
|
|
(void)service_id;
|
|
|
|
tr_debug("Service %d, send CoAP response", service_id);
|
|
if (!request_ptr || !handle) {
|
|
tr_error("invalid params");
|
|
return -1;
|
|
}
|
|
|
|
transaction_ptr = transaction_find_server(request_ptr->msg_id);
|
|
|
|
if (!transaction_ptr) {
|
|
tr_error("response transaction not found");
|
|
return -2;
|
|
}
|
|
dst_addr.addr_ptr = transaction_ptr->remote_address;
|
|
dst_addr.addr_len = 16;
|
|
dst_addr.type = SN_NSDL_ADDRESS_TYPE_IPV6;
|
|
dst_addr.port = transaction_ptr->remote_port;
|
|
|
|
response = sn_coap_build_response(handle->coap, request_ptr, message_code);
|
|
if( !response ){
|
|
return -1;
|
|
}
|
|
response->payload_len = payload_len;
|
|
response->payload_ptr = (uint8_t *) payload_ptr; // Cast away const and trust that nsdl doesn't modify...
|
|
coap_service_build_content_format(response, content_type);
|
|
|
|
data_len = sn_coap_builder_calc_needed_packet_data_size(response);
|
|
data_ptr = own_alloc(data_len);
|
|
if (data_len > 0 && !data_ptr) {
|
|
sn_coap_parser_release_allocated_coap_msg_mem(handle->coap, response);
|
|
//TODO deallocate stuff i quess
|
|
return -1;
|
|
}
|
|
sn_coap_protocol_build(handle->coap, &dst_addr, data_ptr, response, transaction_ptr);
|
|
sn_coap_parser_release_allocated_coap_msg_mem(handle->coap, response);
|
|
handle->sn_coap_tx_callback(data_ptr, data_len, &dst_addr, transaction_ptr);
|
|
sn_coap_parser_release_allocated_coap_msg_mem(handle->coap, request_ptr);
|
|
transaction_delete(transaction_ptr);
|
|
own_free(data_ptr);
|
|
return 0;
|
|
}
|
|
|
|
int8_t coap_message_handler_exec(coap_msg_handler_t *handle, uint32_t current_time){
|
|
if( !handle ){
|
|
return -1;
|
|
}
|
|
return sn_coap_protocol_exec(handle->coap, current_time);
|
|
}
|
|
|
|
static void coap_service_build_content_format(sn_coap_hdr_s *header, sn_coap_content_format_e format)
|
|
{
|
|
header->content_format = format;
|
|
|
|
// if (format == COAP_CT_NONE) {
|
|
// return;
|
|
// }
|
|
// if (format == 0) { /* text/plain */
|
|
// header->content_type_len = 0;
|
|
// } else if (format <= 0xff) {
|
|
// header->content_type_ptr[0] = format;
|
|
// header->content_type_len = 1;
|
|
// } else {
|
|
// header->content_type_ptr[0] = format >> 8;
|
|
// header->content_type_ptr[1] = format & 0xff;
|
|
// header->content_type_len = 2;
|
|
// }
|
|
}
|