blob: e45c7dcfe54512cef4aaa07bfbc5d50395d824ad [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation
*
* Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
* 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 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 the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE 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.
*
* This file is part of the Contiki operating system.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "os/mynewt.h"
#include "oic/port/mynewt/config.h"
/* OIC Stack headers */
#include "oic/oc_buffer.h"
#include "oic/oc_ri.h"
#include "messaging/coap/engine.h"
#ifdef OC_CLIENT
#include "oic/oc_client_state.h"
#endif
/*---------------------------------------------------------------------------*/
/*- Internal API ------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
int
coap_receive(struct os_mbuf **mp)
{
/* static declaration reduces stack peaks and program code size */
/* this way the packet can be treated as pointer as usual */
struct os_mbuf *m;
static struct coap_packet_rx message[1];
static struct coap_packet response[1];
static coap_transaction_t *transaction = NULL;
struct os_mbuf *rsp;
struct oc_endpoint endpoint; /* XXX */
erbium_status_code = NO_ERROR;
OC_LOG(INFO, "CoAP: received datalen=%u\n", OS_MBUF_PKTLEN(*mp));
memcpy(&endpoint, OC_MBUF_ENDPOINT(*mp),
oc_endpoint_size(OC_MBUF_ENDPOINT(*mp)));
erbium_status_code = coap_parse_message(message, mp);
if (erbium_status_code != NO_ERROR) {
goto out;
}
m = *mp;
/*TODO duplicates suppression, if required by application */
OC_LOG(DEBUG, " Parsed: CoAP version: %u, token: 0x%02X%02X, mid: %u\n",
message->version, message->token[0], message->token[1],
message->mid);
switch (message->type) {
case COAP_TYPE_CON:
OC_LOG(DEBUG, " type: CON\n");
break;
case COAP_TYPE_NON:
OC_LOG(DEBUG, " type: NON\n");
break;
case COAP_TYPE_ACK:
OC_LOG(DEBUG, " type: ACK\n");
break;
case COAP_TYPE_RST:
OC_LOG(DEBUG, " type: RST\n");
break;
default:
break;
}
if (message->code >= COAP_GET && message->code <= COAP_DELETE) {
/* handle requests */
switch (message->code) {
case COAP_GET:
OC_LOG(DEBUG, " method: GET\n");
break;
case COAP_PUT:
OC_LOG(DEBUG, " method: PUT\n");
break;
case COAP_POST:
OC_LOG(DEBUG, " method: POST\n");
break;
case COAP_DELETE:
OC_LOG(DEBUG, " method: DELETE\n");
break;
}
OC_LOG(DEBUG, " Payload: %d bytes\n", message->payload_len);
/* use transaction buffer for response to confirmable request */
transaction = coap_new_transaction(message->mid, OC_MBUF_ENDPOINT(m));
if (!transaction) {
erbium_status_code = SERVICE_UNAVAILABLE_5_03;
coap_error_message = "NoFreeTraBuffer";
goto out;
}
uint32_t block_num = 0;
uint16_t block_size = COAP_MAX_BLOCK_SIZE;
uint32_t block_offset = 0;
int32_t new_offset = 0;
/* prepare response */
if (message->type == COAP_TYPE_CON) {
/* reliable CON requests are answered with an ACK */
coap_init_message(response, COAP_TYPE_ACK, CONTENT_2_05,
message->mid);
} else {
/* unreliable NON requests are answered with a NON */
coap_init_message(response, COAP_TYPE_NON, CONTENT_2_05,
coap_get_mid());
/* mirror token */
}
if (message->token_len) {
coap_set_token(response, message->token, message->token_len);
/* get offset for blockwise transfers */
}
if (coap_get_header_block2(message, &block_num, NULL,
&block_size, &block_offset)) {
OC_LOG(DEBUG, " Blockwise: block request %u (%u/%u) @ %u bytes\n",
(unsigned int) block_num, block_size,
COAP_MAX_BLOCK_SIZE, (unsigned int) block_offset);
block_size = MIN(block_size, COAP_MAX_BLOCK_SIZE);
new_offset = block_offset;
}
if (oc_ri_invoke_coap_entity_handler(message, response, &new_offset,
OC_MBUF_ENDPOINT(m))) {
if (erbium_status_code == NO_ERROR) {
/*
* TODO coap_handle_blockwise(request, response,
* start_offset, end_offset);
*/
/* resource is unaware of Block1 */
if (IS_OPTION(message, COAP_OPTION_BLOCK1) &&
response->code < BAD_REQUEST_4_00 &&
!IS_OPTION(response, COAP_OPTION_BLOCK1)) {
OC_LOG(ERROR, " Block1 option NOT IMPLEMENTED\n");
erbium_status_code = NOT_IMPLEMENTED_5_01;
coap_error_message = "NoBlock1Support";
/* client requested Block2 transfer */
} else if (IS_OPTION(message, COAP_OPTION_BLOCK2)) {
/*
* Unchanged new_offset indicates that resource is
* unaware of blockwise transfer
*/
if (new_offset == block_offset) {
OC_LOG(DEBUG, " Block: unaware resource %u/%u\n",
response->payload_len, block_size);
if (block_offset >= response->payload_len) {
response->code = BAD_OPTION_4_02;
rsp = os_msys_get_pkthdr(0, 0);
if (rsp) {
os_mbuf_copyinto(rsp, 0, "BlockOutOfScope", 15);
response->payload_m = rsp;
response->payload_len = 15;
}
/* a const char str[] and sizeof(str)
produces larger code size */
} else {
coap_set_header_block2(response, block_num,
response->payload_len - block_offset >
block_size, block_size);
response->payload_len = MIN(response->payload_len -
block_offset,
block_size);
} /* if(valid offset) */
/* resource provides chunk-wise data */
} else {
OC_LOG(DEBUG, " Block: aware resource, off %d\n",
(int) new_offset);
coap_set_header_block2(response, block_num,
new_offset != -1 ||
response->payload_len > block_size,
block_size);
if (response->payload_len > block_size) {
response->payload_len = block_size;
}
} /* if(resource aware of blockwise) */
/* Resource requested Block2 transfer */
} else if (new_offset != 0) {
OC_LOG(DEBUG, " block: no block option, using block sz %u\n",
COAP_MAX_BLOCK_SIZE);
coap_set_header_block2(response, 0, new_offset != -1,
COAP_MAX_BLOCK_SIZE);
response->payload_len = MIN(response->payload_len,
COAP_MAX_BLOCK_SIZE);
} /* blockwise transfer handling */
} /* no errors/hooks */
/* successful service callback */
/* serialize response */
}
if (erbium_status_code == NO_ERROR) {
if (coap_serialize_message(response, transaction->m)) {
erbium_status_code = PACKET_SERIALIZATION_ERROR;
}
transaction->type = response->type;
}
} else { // Fix this
/* handle responses */
if (message->type == COAP_TYPE_CON) {
erbium_status_code = EMPTY_ACK_RESPONSE;
} else if (message->type == COAP_TYPE_ACK) {
/* transactions are closed through lookup below */
} else if (message->type == COAP_TYPE_RST) {
#ifdef OC_SERVER
/* cancel possible subscriptions */
coap_remove_observer_by_mid(OC_MBUF_ENDPOINT(m), message->mid);
#endif
}
/* Open transaction now cleared for ACK since mid matches */
if ((transaction = coap_get_transaction_by_mid(message->mid))) {
coap_clear_transaction(transaction);
}
/* if(ACKed transaction) */
transaction = NULL;
#ifdef OC_CLIENT
/*
* ACKs and RSTs sent to oc_ri.. RSTs cleared, ACKs sent to
* client.
*/
oc_ri_invoke_client_cb(message, OC_MBUF_ENDPOINT(m));
#endif
} /* request or response */
out:
/* if(parsed correctly) */
if (erbium_status_code == NO_ERROR) {
if (transaction) { // Server transactions sent from here
coap_send_transaction(transaction);
}
} else if (erbium_status_code == CLEAR_TRANSACTION) {
OC_LOG(DEBUG, " Clearing transaction for manual response\n");
/* used in server for separate response */
coap_clear_transaction(transaction);
}
#ifdef OC_CLIENT
else if (erbium_status_code == EMPTY_ACK_RESPONSE) {
coap_init_message(response, COAP_TYPE_ACK, 0, message->mid);
struct os_mbuf *m_rsp = oc_allocate_mbuf(&endpoint);
if (m_rsp) {
if (!coap_serialize_message(response, m_rsp)) {
coap_send_message(m_rsp, 0);
} else {
os_mbuf_free_chain(m_rsp);
}
}
}
#endif /* OC_CLIENT */
#ifdef OC_SERVER
else { // framework errors handled here
coap_message_type_t reply_type = COAP_TYPE_RST;
coap_clear_transaction(transaction);
coap_init_message(response, reply_type, SERVICE_UNAVAILABLE_5_03,
message->mid);
struct os_mbuf *m_rsp = oc_allocate_mbuf(&endpoint);
if (m_rsp) {
if (!coap_serialize_message(response, m_rsp)) {
coap_send_message(m_rsp, 0);
} else {
os_mbuf_free_chain(m_rsp);
}
}
}
#endif /* OC_SERVER */
/* if(new data) */
return erbium_status_code;
}
void
coap_engine_init(void)
{
coap_init_connection();
coap_transaction_init();
#ifdef OC_SERVER
#if MYNEWT_VAL(OC_SEPARATE_RESPONSES)
coap_separate_init();
#endif
coap_observe_init();
#endif
}