blob: c24bedb5502a16317c77ec4c17b63dbfdb375e28 [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 <string.h>
#include "os/mynewt.h"
#include "oic/port/mynewt/config.h"
#ifdef OC_SERVER
#include "oic/messaging/coap/observe.h"
#include "oic/messaging/coap/oc_coap.h"
#include "oic/oc_rep.h"
#include "oic/oc_ri.h"
/*-------------------*/
uint64_t observe_counter = 3;
/*---------------------------------------------------------------------------*/
static SLIST_HEAD(, coap_observer) oc_observers;
static struct os_mempool coap_observer_pool;
static uint8_t coap_observer_area[OS_MEMPOOL_BYTES(COAP_MAX_OBSERVERS,
sizeof(coap_observer_t))];
/*---------------------------------------------------------------------------*/
/*- Internal API ------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
static int
add_observer(oc_resource_t *resource, oc_endpoint_t *endpoint,
const uint8_t *token, size_t token_len, const char *uri,
int uri_len)
{
/* Remove existing observe relationship, if any. */
int dup = coap_remove_observer_by_uri(endpoint, uri);
coap_observer_t *o = os_memblock_get(&coap_observer_pool);
if (o) {
int max = sizeof(o->url) - 1;
if (max > uri_len) {
max = uri_len;
}
memcpy(o->url, uri, max);
o->url[max] = 0;
memcpy(&o->endpoint, endpoint, oc_endpoint_size(endpoint));
o->token_len = token_len;
memcpy(o->token, token, token_len);
o->last_mid = 0;
o->obs_counter = observe_counter;
o->resource = resource;
resource->num_observers++;
OC_LOG(DEBUG, "Adding observer (%u/%u) for /%s [0x%02X%02X]\n",
coap_observer_pool.mp_num_blocks - coap_observer_pool.mp_num_free,
coap_observer_pool.mp_num_blocks, o->url, o->token[0], o->token[1]);
SLIST_INSERT_HEAD(&oc_observers, o, next);
return dup;
}
return -1;
}
/*---------------------------------------------------------------------------*/
/*- Removal -----------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
void
coap_remove_observer(coap_observer_t *o)
{
OC_LOG(DEBUG, "Removing observer for /%s [0x%02X%02X]\n",
o->url, o->token[0], o->token[1]);
SLIST_REMOVE(&oc_observers, o, coap_observer, next);
os_memblock_put(&coap_observer_pool, o);
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_client(oc_endpoint_t *endpoint)
{
int removed = 0;
coap_observer_t *obs, *next;
obs = SLIST_FIRST(&oc_observers);
while (obs) {
next = SLIST_NEXT(obs, next);
if (memcmp(&obs->endpoint, endpoint, oc_endpoint_size(endpoint)) == 0) {
obs->resource->num_observers--;
coap_remove_observer(obs);
removed++;
}
obs = next;
}
return removed;
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_token(oc_endpoint_t *endpoint, uint8_t *token,
size_t token_len)
{
int removed = 0;
coap_observer_t *obs, *next;
obs = SLIST_FIRST(&oc_observers);
while (obs) {
next = SLIST_NEXT(obs, next);
if (memcmp(&obs->endpoint, endpoint, oc_endpoint_size(endpoint)) == 0 &&
obs->token_len == token_len &&
memcmp(obs->token, token, token_len) == 0) {
obs->resource->num_observers--;
coap_remove_observer(obs);
removed++;
break;
}
obs = next;
}
return removed;
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_uri(oc_endpoint_t *endpoint, const char *uri)
{
int removed = 0;
coap_observer_t *obs, *next;
obs = SLIST_FIRST(&oc_observers);
while (obs) {
next = SLIST_NEXT(obs, next);
if (((memcmp(&obs->endpoint, endpoint,
oc_endpoint_size(endpoint)) == 0)) &&
(obs->url == uri || memcmp(obs->url, uri, strlen(obs->url)) == 0)) {
obs->resource->num_observers--;
coap_remove_observer(obs);
removed++;
}
obs = next;
}
return removed;
}
/*---------------------------------------------------------------------------*/
int
coap_remove_observer_by_mid(oc_endpoint_t *endpoint, uint16_t mid)
{
int removed = 0;
coap_observer_t *obs, *next;
obs = SLIST_FIRST(&oc_observers);
while (obs) {
next = SLIST_NEXT(obs, next);
if (memcmp(&obs->endpoint, endpoint, oc_endpoint_size(endpoint)) == 0 &&
obs->last_mid == mid) {
obs->resource->num_observers--;
coap_remove_observer(obs);
removed++;
break;
}
obs = next;
}
return removed;
}
/*---------------------------------------------------------------------------*/
/*- Notification ------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
int
coap_notify_observers(oc_resource_t *resource,
oc_response_buffer_t *response_buf,
oc_endpoint_t *endpoint)
{
int num_observers = 0;
oc_request_t request = {};
oc_response_t response = {};
oc_response_buffer_t response_buffer;
struct os_mbuf *m = NULL;
if (resource) {
if (!resource->num_observers) {
OC_LOG(DEBUG, "coap_notify_observers: no observers left\n");
return 0;
}
num_observers = resource->num_observers;
}
response.separate_response = 0;
if (!response_buf && resource) {
OC_LOG(DEBUG, "coap_notify_observers: Issue GET request to resource\n");
/* performing GET on the resource */
m = os_msys_get_pkthdr(0, 0);
if (!m) {
/* XXX count */
return num_observers;
}
response_buffer.buffer = m;
response_buffer.block_offset = NULL;
response.response_buffer = &response_buffer;
request.resource = resource;
request.response = &response;
oc_rep_new(m);
resource->get_handler(&request, resource->default_interface);
response_buf = &response_buffer;
if (response_buf->code == OC_IGNORE) {
OC_LOG(ERROR, "coap_notify_observers: Resource ignored request\n");
os_mbuf_free_chain(m);
return num_observers;
}
}
coap_observer_t *obs = NULL;
/* iterate over observers */
for (obs = SLIST_FIRST(&oc_observers); obs; obs = SLIST_NEXT(obs, next)) {
/* skip if neither resource nor endpoint match */
if ((resource && resource != obs->resource) ||
(endpoint && memcmp(&obs->endpoint, endpoint,
oc_endpoint_size(endpoint)) != 0)) {
continue;
}
num_observers = obs->resource->num_observers;
#if MYNEWT_VAL(OC_SEPARATE_RESPONSES)
if (response.separate_response != NULL &&
response_buf->code == oc_status_code(OC_STATUS_OK)) {
struct coap_packet_rx req[1];
req->block1_num = 0;
req->block1_size = 0;
req->block2_num = 0;
req->block2_size = 0;
req->type = COAP_TYPE_NON;
req->code = CONTENT_2_05;
req->mid = 0;
memcpy(req->token, obs->token, obs->token_len);
req->token_len = obs->token_len;
OC_LOG(DEBUG, "Resource is SLOW; creating separate response\n");
if (coap_separate_accept(req, response.separate_response,
&obs->endpoint, 0) == 1) {
response.separate_response->active = 1;
}
} else {
#endif /* OC_SEPARATE_RESPONSES */
OC_LOG(DEBUG, "coap_notify_observers: notifying observer\n");
coap_transaction_t *transaction = NULL;
if (response_buf && (transaction = coap_new_transaction(
coap_get_mid(), &obs->endpoint))) {
/* update last MID for RST matching */
obs->last_mid = transaction->mid;
/* prepare response */
/* build notification */
coap_packet_t notification[1];
/* this way the packet can be treated as pointer as usual */
coap_init_message(notification, COAP_TYPE_NON, CONTENT_2_05, 0);
notification->mid = transaction->mid;
if (!oc_endpoint_use_tcp(&obs->endpoint) &&
obs->obs_counter % COAP_OBSERVE_REFRESH_INTERVAL == 0) {
OC_LOG(DEBUG, "coap_observe_notify: forcing CON "
"notification to check for client liveness\n");
notification->type = COAP_TYPE_CON;
}
coap_set_payload(notification, response_buf->buffer,
OS_MBUF_PKTLEN(response_buf->buffer));
coap_set_status_code(notification, response_buf->code);
coap_set_header_content_format(notification, APPLICATION_CBOR);
if (notification->code < BAD_REQUEST_4_00 &&
obs->resource->num_observers) {
coap_set_header_observe(notification, (obs->obs_counter)++);
observe_counter++;
} else {
coap_set_header_observe(notification, 1);
}
coap_set_token(notification, obs->token, obs->token_len);
if (!coap_serialize_message(notification, transaction->m)) {
transaction->type = notification->type;
coap_send_transaction(transaction);
} else {
coap_clear_transaction(transaction);
}
}
#if MYNEWT_VAL(OC_SEPARATE_RESPONSES)
}
#endif
}
if (m) {
os_mbuf_free_chain(m);
}
return num_observers;
}
/*---------------------------------------------------------------------------*/
int
coap_observe_handler(struct coap_packet_rx *coap_req, coap_packet_t *coap_res,
oc_resource_t *resource, oc_endpoint_t *endpoint)
{
int dup = -1;
if (coap_req->code == COAP_GET &&
coap_res->code < 128) { /* GET request and response without error code */
if (IS_OPTION(coap_req, COAP_OPTION_OBSERVE)) {
if (coap_req->observe == 0) {
char uri[COAP_MAX_URI];
int uri_len;
uri_len = coap_get_header_uri_path(coap_req, uri, sizeof(uri));
dup = add_observer(resource, endpoint, coap_req->token,
coap_req->token_len, uri, uri_len);
} else if (coap_req->observe == 1) {
/* remove client if it is currently observe */
dup = coap_remove_observer_by_token(endpoint, coap_req->token,
coap_req->token_len);
}
}
}
return dup;
}
/*---------------------------------------------------------------------------*/
void
coap_observe_init(void)
{
os_mempool_init(&coap_observer_pool, COAP_MAX_OBSERVERS,
sizeof(coap_observer_t), coap_observer_area, "coap_obs");
}
#endif /* OC_SERVER */