blob: b2564ff39932e194a9abf79f474a016867e3c69d [file] [log] [blame]
/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#include <stdbool.h>
#include <stddef.h>
#include <strings.h>
#include "os/mynewt.h"
#include "oic/port/mynewt/config.h"
#include "oic/messaging/coap/constants.h"
#include "messaging/coap/engine.h"
#include "oic/messaging/coap/oc_coap.h"
#include "oic/port/oc_random.h"
#include "port/oc_clock.h"
#include "port/mynewt/adaptor.h"
#include "oic/oc_buffer.h"
#include "oic/oc_core_res.h"
#include "oic/oc_discovery.h"
#include "oic/oc_ri.h"
#include "oic/oc_uuid.h"
#include "oic/oc_ri_const.h"
#include "api/oc_priv.h"
#ifdef OC_SECURITY
#include "security/oc_acl.h"
#include "security/oc_dtls.h"
#endif /* OC_SECURITY */
#ifdef OC_SERVER
static SLIST_HEAD(, oc_resource) oc_app_resources =
SLIST_HEAD_INITIALIZER(&oc_app_resources);
static struct os_mempool oc_resource_pool;
static uint8_t oc_resource_area[OS_MEMPOOL_BYTES(MAX_APP_RESOURCES,
sizeof(oc_resource_t))];
static void periodic_observe_handler(struct os_event *ev);
#endif /* OC_SERVER */
#ifdef OC_CLIENT
#include "oc_client_state.h"
static SLIST_HEAD(, oc_client_cb) oc_client_cbs;
static struct os_mempool oc_client_cb_pool;
static uint8_t oc_client_cb_area[OS_MEMPOOL_BYTES(MAX_NUM_CONCURRENT_REQUESTS,
sizeof(oc_client_cb_t))];
#endif /* OC_CLIENT */
// TODO: Define and use a complete set of error codes.
int oc_stack_errno;
static const unsigned int oc_coap_status_codes[__NUM_OC_STATUS_CODES__] = {
/* OK_200 */
[OC_STATUS_OK] = CONTENT_2_05,
/* CREATED_201 */
[OC_STATUS_CREATED] = CREATED_2_01,
/* NO_CONTENT_204 */
[OC_STATUS_CHANGED] = CHANGED_2_04,
/* NO_CONTENT_204 */
[OC_STATUS_DELETED] = DELETED_2_02,
/* NOT_MODIFIED_304 */
[OC_STATUS_NOT_MODIFIED] = VALID_2_03,
/* BAD_REQUEST_400 */
[OC_STATUS_BAD_REQUEST] = BAD_REQUEST_4_00,
/* UNAUTHORIZED_401 */
[OC_STATUS_UNAUTHORIZED] = UNAUTHORIZED_4_01,
/* BAD_REQUEST_400 */
[OC_STATUS_BAD_OPTION] = BAD_OPTION_4_02,
/* FORBIDDEN_403 */
[OC_STATUS_FORBIDDEN] = FORBIDDEN_4_03,
/* NOT_FOUND_404 */
[OC_STATUS_NOT_FOUND] = NOT_FOUND_4_04,
/* METHOD_NOT_ALLOWED_405 */
[OC_STATUS_METHOD_NOT_ALLOWED] = METHOD_NOT_ALLOWED_4_05,
/* NOT_ACCEPTABLE_406 */
[OC_STATUS_NOT_ACCEPTABLE] = NOT_ACCEPTABLE_4_06,
/* REQUEST_ENTITY_TOO_LARGE_413 */
[OC_STATUS_REQUEST_ENTITY_TOO_LARGE] = REQUEST_ENTITY_TOO_LARGE_4_13,
/* UNSUPPORTED_MEDIA_TYPE_415 */
[OC_STATUS_UNSUPPORTED_MEDIA_TYPE] = UNSUPPORTED_MEDIA_TYPE_4_15,
/* INTERNAL_SERVER_ERROR_500 */
[OC_STATUS_INTERNAL_SERVER_ERROR] = INTERNAL_SERVER_ERROR_5_00,
/* NOT_IMPLEMENTED_501 */
[OC_STATUS_NOT_IMPLEMENTED] = NOT_IMPLEMENTED_5_01,
/* BAD_GATEWAY_502 */
[OC_STATUS_BAD_GATEWAY] = BAD_GATEWAY_5_02,
/* SERVICE_UNAVAILABLE_503 */
[OC_STATUS_SERVICE_UNAVAILABLE] = SERVICE_UNAVAILABLE_5_03,
/* GATEWAY_TIMEOUT_504 */
[OC_STATUS_GATEWAY_TIMEOUT] = GATEWAY_TIMEOUT_5_04,
/* INTERNAL_SERVER_ERROR_500 */
[OC_STATUS_PROXYING_NOT_SUPPORTED] = PROXYING_NOT_SUPPORTED_5_05
};
#ifdef OC_SERVER
oc_resource_t *
oc_ri_get_app_resources(void)
{
return SLIST_FIRST(&oc_app_resources);
}
#endif
int
oc_status_code(oc_status_t key)
{
return oc_coap_status_codes[key];
}
int
oc_ri_get_query_nth_key_value(const char *query, int query_len, char **key,
int *key_len, char **value, int *value_len, int n)
{
int next_pos = -1;
int i = 0;
char *start = (char *)query, *current, *end = (char *)query + query_len;
current = start;
while (i < (n - 1) && current != NULL) {
current = memchr(start, '&', end - start);
i++;
start = current + 1;
}
current = memchr(start, '=', end - start);
if (current != NULL) {
*key_len = current - start;
*key = start;
*value = current + 1;
current = memchr(*value, '&', end - *value);
if (current == NULL) {
*value_len = end - *value;
} else {
*value_len = current - *value;
}
next_pos = *value + *value_len - query + 1;
}
return next_pos;
}
int
oc_ri_get_query_value(const char *query, int query_len, const char *key,
char **value)
{
int next_pos = 0, found = -1, kl, vl;
char *k;
while (next_pos < query_len) {
next_pos += oc_ri_get_query_nth_key_value(
query + next_pos, query_len - next_pos, &k, &kl, value, &vl, 1);
if (next_pos == -1)
return -1;
if (kl == strlen(key) && strncasecmp(key, k, kl) == 0) {
found = vl;
break;
}
}
return found;
}
static void
start_processes(void)
{
coap_engine_init();
#ifdef OC_SECURITY
oc_process_start(&oc_dtls_handler, NULL);
#endif
}
static void
stop_processes(void)
{
#ifdef OC_SECURITY
oc_process_exit(&oc_dtls_handler);
#endif
}
#ifdef OC_SERVER
oc_resource_t *
oc_ri_get_app_resource_by_uri(const char *uri)
{
oc_resource_t *res;
SLIST_FOREACH(res, &oc_app_resources, next) {
if (oc_string_len(res->uri) == strlen(uri) &&
strncmp(uri, oc_string(res->uri), strlen(uri)) == 0)
return res;
}
return NULL;
}
#endif
void
oc_ri_mem_init(void)
{
#ifdef OC_SERVER
os_mempool_init(&oc_resource_pool, MAX_APP_RESOURCES, sizeof(oc_resource_t),
oc_resource_area, "oc_res");
#endif
#ifdef OC_CLIENT
os_mempool_init(&oc_client_cb_pool, MAX_NUM_CONCURRENT_REQUESTS,
sizeof(oc_client_cb_t), oc_client_cb_area, "oc_cl_cbs");
oc_rep_init();
#endif
oc_buffer_init();
}
void
oc_ri_init(void)
{
#ifdef OC_CLIENT
SLIST_INIT(&oc_client_cbs);
#endif
start_processes();
oc_create_discovery_resource();
}
void
oc_ri_shutdown(void)
{
oc_random_destroy();
stop_processes();
}
#ifdef OC_SERVER
oc_resource_t *
oc_ri_alloc_resource(void)
{
struct oc_resource *resource;
resource = os_memblock_get(&oc_resource_pool);
if (resource) {
os_callout_init(&resource->callout, oc_evq_get(),
periodic_observe_handler, resource);
}
return resource;
}
void
oc_ri_delete_resource(oc_resource_t *resource)
{
oc_resource_t *tmp;
SLIST_FOREACH(tmp, &oc_app_resources, next) {
if (tmp == resource) {
SLIST_REMOVE(&oc_app_resources, tmp, oc_resource, next);
break;
}
}
os_memblock_put(&oc_resource_pool, resource);
}
bool
oc_ri_add_resource(oc_resource_t *resource)
{
bool valid = true;
if (!resource->get_handler && !resource->put_handler &&
!resource->post_handler && !resource->delete_handler) {
valid = false;
}
if (resource->properties & OC_PERIODIC &&
resource->observe_period_mseconds == 0) {
valid = false;
}
if (valid) {
SLIST_INSERT_HEAD(&oc_app_resources, resource, next);
}
return valid;
}
#endif /* OC_SERVER */
#ifdef OC_SERVER
static void
periodic_observe_handler(struct os_event *ev)
{
struct oc_resource *resource;
resource = ev->ev_arg;
if (coap_notify_observers(resource, NULL, NULL)) {
os_callout_reset(&resource->callout,
(resource->observe_period_mseconds * OS_TICKS_PER_SEC)/1000);
}
}
#endif
oc_interface_mask_t
oc_ri_get_interface_mask(char *iface, int if_len)
{
oc_interface_mask_t interface = 0;
if (OC_BASELINE_IF_LEN == if_len &&
strncmp(iface, OC_RSRVD_IF_BASELINE, if_len) == 0)
interface |= OC_IF_BASELINE;
if (OC_LL_IF_LEN == if_len && strncmp(iface, OC_RSRVD_IF_LL, if_len) == 0)
interface |= OC_IF_LL;
if (OC_B_IF_LEN == if_len && strncmp(iface, OC_RSRVD_IF_B, if_len) == 0)
interface |= OC_IF_B;
if (OC_R_IF_LEN == if_len && strncmp(iface, OC_RSRVD_IF_R, if_len) == 0)
interface |= OC_IF_R;
if (OC_RW_IF_LEN == if_len && strncmp(iface, OC_RSRVD_IF_RW, if_len) == 0)
interface |= OC_IF_RW;
if (OC_A_IF_LEN == if_len && strncmp(iface, OC_RSRVD_IF_A, if_len) == 0)
interface |= OC_IF_A;
if (OC_S_IF_LEN == if_len && strncmp(iface, OC_RSRVD_IF_S, if_len) == 0)
interface |= OC_IF_S;
return interface;
}
static bool
does_interface_support_method(oc_resource_t *resource,
oc_interface_mask_t interface, oc_method_t method)
{
bool supported = true;
switch (interface) {
/* Per section 7.5.3 of the OCF Core spec, the following three interfaces
* are RETRIEVE-only.
*/
case OC_IF_LL:
case OC_IF_S:
case OC_IF_R:
if (method != OC_GET)
supported = false;
break;
/* Per section 7.5.3 of the OCF Core spec, the following three interfaces
* support RETRIEVE, UPDATE.
* TODO: Refine logic below after adding logic that identifies
* and handles CREATE requests using PUT/POST.
*/
case OC_IF_RW:
case OC_IF_B:
case OC_IF_BASELINE:
/* Per section 7.5.3 of the OCF Core spec, the following interface
* supports CREATE, RETRIEVE and UPDATE.
*/
case OC_IF_A:
break;
}
return supported;
}
/**
* Determines if the endpoint meets the specified resource's minimum transport
* layer security requirements.
*
* @param oe The endpoint to check.
* @param res The resource being accessed.
*
* @return true if the endpoint has sufficient security;
* false if the security requirements are not met.
*/
static bool
oc_ri_check_trans_security(const oc_endpoint_t *oe,
const oc_resource_t *res)
{
#if MYNEWT_VAL(OC_TRANS_SECURITY)
oc_resource_properties_t res_sec_flags;
res_sec_flags = res->properties & OC_TRANS_SEC_MASK;
if (res_sec_flags == 0) {
return true;
}
if ((oc_get_trans_security(oe) ^ res_sec_flags) != 0) {
return false;
}
#endif
return true;
}
bool
oc_ri_invoke_coap_entity_handler(struct coap_packet_rx *request,
coap_packet_t *response, int32_t *offset,
oc_endpoint_t *endpoint)
{
/* Flags that capture status along various stages of processing
* the request.
*/
bool method_impl = true, bad_request = false, success = true;
bool authorized = true;
/* This function is a server-side entry point solely for requests.
* Hence, "code" contains the CoAP method code.
*/
oc_method_t method = request->code;
/* Initialize request/response objects to be sent up to the app layer. */
oc_request_t request_obj;
oc_response_buffer_t response_buffer;
oc_response_t response_obj;
struct os_mbuf *m = NULL;
response_buffer.buffer = NULL;
response_buffer.block_offset = offset;
response_buffer.code = 0;
response_buffer.response_length = 0;
response_obj.separate_response = 0;
response_obj.response_buffer = &response_buffer;
request_obj.response = &response_obj;
request_obj.query_len = 0;
request_obj.resource = 0;
request_obj.origin = endpoint;
request_obj.packet = request;
/* Initialize OCF interface selector. */
oc_interface_mask_t interface = 0;
/* Obtain request uri from the CoAP packet. */
char uri_path[COAP_MAX_URI];
int uri_path_len = coap_get_header_uri_path(request, uri_path,
sizeof(uri_path));
/* Obtain query string from CoAP packet. */
char uri_query[COAP_MAX_URI_QUERY];
int uri_query_len = coap_get_header_uri_query(request, uri_query,
sizeof(uri_query));
if (uri_query_len) {
request_obj.query = uri_query;
request_obj.query_len = uri_query_len;
/* Check if query string includes interface selection. */
char *iface;
int if_len = oc_ri_get_query_value(uri_query, uri_query_len, "if", &iface);
if (if_len != -1) {
interface |= oc_ri_get_interface_mask(iface, if_len);
}
}
oc_resource_t *resource, *cur_resource = NULL;
/* If there were no errors thus far, attempt to locate the specific
* resource object that will handle the request using the request uri.
*/
/* Check against list of declared core resources.
*/
if (!bad_request) {
int i;
for (i = 0; i < NUM_OC_CORE_RESOURCES; i++) {
resource = oc_core_get_resource_by_index(i);
if (oc_string_len(resource->uri) == (uri_path_len + 1) &&
strncmp((const char *)oc_string(resource->uri) + 1, uri_path,
uri_path_len) == 0) {
request_obj.resource = cur_resource = resource;
break;
}
}
}
#ifdef OC_SERVER
/* Check against list of declared application resources.
*/
if (!cur_resource && !bad_request) {
SLIST_FOREACH(resource, &oc_app_resources, next) {
if (oc_string_len(resource->uri) == (uri_path_len + 1) &&
strncmp((const char *)oc_string(resource->uri) + 1, uri_path,
uri_path_len) == 0) {
request_obj.resource = cur_resource = resource;
break;
}
}
}
#endif
if (cur_resource) {
/* If there was no interface selection, pick the "default interface". */
if (interface == 0)
interface = cur_resource->default_interface;
/* Found the matching resource object. Now verify that:
* 1) the selected interface is one that is supported by
* the resource, and,
* 2) the selected interface supports the request method.
*
* If not, return a 4.00 response.
*/
if (((interface & ~cur_resource->interfaces) != 0) ||
!does_interface_support_method(cur_resource, interface, method))
bad_request = true;
}
m = os_msys_get_pkthdr(0, 0);
if (!m) {
bad_request = true;
}
response_buffer.buffer = m;
if (cur_resource && !bad_request) {
/* Process a request against a valid resource, request payload, and
* interface.
*/
/* Initialize oc_rep with a buffer to hold the response payload. "buffer"
* points to memory allocated in the messaging layer for the "CoAP
* Transaction" to service this request.
*/
oc_rep_new(m);
#ifdef OC_SECURITY
/* If cur_resource is a coaps:// resource, then query ACL to check if
* the requestor (the subject) is authorized to issue this request to
* the resource.
*/
if ((cur_resource->properties & OC_SECURE) &&
!oc_sec_check_acl(method, cur_resource, endpoint)) {
authorized = false;
} else
#endif
if (!oc_ri_check_trans_security(endpoint, cur_resource)) {
authorized = false;
} else {
/* Invoke a specific request handler for the resource
* based on the request method. If the resource has not
* implemented that method, then return a 4.05 response.
*/
if (method == OC_GET && cur_resource->get_handler) {
cur_resource->get_handler(&request_obj, interface);
} else if (method == OC_POST && cur_resource->post_handler) {
cur_resource->post_handler(&request_obj, interface);
} else if (method == OC_PUT && cur_resource->put_handler) {
cur_resource->put_handler(&request_obj, interface);
} else if (method == OC_DELETE && cur_resource->delete_handler) {
cur_resource->delete_handler(&request_obj, interface);
} else {
method_impl = false;
}
}
}
if (bad_request) {
if (!m) {
OC_LOG(ERROR, "ocri: No bufs\n");
response_buffer.code = oc_status_code(OC_STATUS_SERVICE_UNAVAILABLE);
} else {
OC_LOG(ERROR, "ocri: Bad request\n");
/* Return a 4.00 response */
response_buffer.code = oc_status_code(OC_STATUS_BAD_REQUEST);
}
success = false;
} else if (!cur_resource) {
OC_LOG(ERROR, "ocri: Could not find resource\n");
/* Return a 4.04 response if the requested resource was not found */
response_buffer.response_length = 0;
response_buffer.code = oc_status_code(OC_STATUS_NOT_FOUND);
success = false;
} else if (!method_impl) {
OC_LOG(ERROR, "ocri: Could not find method\n");
/* Return a 4.05 response if the resource does not implement the
* request method.
*/
response_buffer.response_length = 0;
response_buffer.code = oc_status_code(OC_STATUS_METHOD_NOT_ALLOWED);
success = false;
}
else if (!authorized) {
OC_LOG(ERROR, "ocri: Subject not authorized\n");
/* If the requestor (subject) does not have access granted via an
* access control entry in the ACL, then it is not authorized to
* access the resource. A 4.03 response is sent.
*/
response_buffer.response_length = 0;
response_buffer.code = oc_status_code(OC_STATUS_FORBIDDEN);
success = false;
}
#ifdef OC_SERVER
/* If a GET request was successfully processed, then check its
* observe option.
*/
uint32_t observe = 2;
if (success && coap_get_header_observe(request, &observe)) {
/* Check if the resource is OBSERVABLE */
if (cur_resource->properties & OC_OBSERVABLE) {
/* If the observe option is set to 0, make an attempt to add the
* requesting client as an observer.
*/
if (observe == 0) {
if (coap_observe_handler(request, response, cur_resource, endpoint) ==
0) {
/* If the resource is marked as periodic observable it means
* it must be polled internally for updates (which would lead to
* notifications being sent). If so, add the resource to a list of
* periodic GET callbacks to utilize the framework's internal
* polling mechanism.
*/
bool set_observe_option = true;
if (cur_resource->properties & OC_PERIODIC) {
os_callout_reset(&cur_resource->callout,
(cur_resource->observe_period_mseconds * OS_TICKS_PER_SEC)/1000);
}
if (set_observe_option) {
coap_set_header_observe(response, 0);
}
}
}
/* If the observe option is set to 1, make an attempt to remove
* the requesting client from the list of observers. In addition,
* remove the resource from the list periodic GET callbacks if it
* is periodic observable.
*/
else if (observe == 1) {
if (coap_observe_handler(request, response, cur_resource, endpoint) >
0) {
if (cur_resource->properties & OC_PERIODIC) {
os_callout_stop(&cur_resource->callout);
}
}
}
}
}
#endif
#if defined(OC_SERVER) && MYNEWT_VAL(OC_SEPARATE_RESPONSES)
/* The presence of a separate response handle here indicates a
* successful handling of the request by a slow resource.
*/
if (response_obj.separate_response != NULL) {
/* Attempt to register a client request to the separate response tracker
* and pass in the observe option (if present) or the value 2 as
* determined by the code block above. Values 0 and 1 result in their
* expected behaviors whereas 2 indicates an absence of an observe
* option and hence a one-off request.
* Following a successful registration, the separate response tracker
* is flagged as "active". In this way, the function that later executes
* out-of-band upon availability of the resource state knows it must
* send out a response with it.
*/
if (coap_separate_accept(request, response_obj.separate_response, endpoint,
observe) == 1)
response_obj.separate_response->active = 1;
} else
#endif /* OC_SERVER && OC_SEPARATE_RESPONSES */
if (response_buffer.code == OC_IGNORE) {
/* If the server-side logic chooses to reject a request, it sends
* below a response code of IGNORE, which results in the messaging
* layer freeing the CoAP transaction associated with the request.
*/
erbium_status_code = CLEAR_TRANSACTION;
} else {
#ifdef OC_SERVER
/* If the recently handled request was a PUT/POST, it conceivably
* altered the resource state, so attempt to notify all observers
* of that resource with the change.
*/
if ((method == OC_PUT || method == OC_POST) &&
response_buffer.code < oc_status_code(OC_STATUS_BAD_REQUEST)) {
coap_notify_observers(cur_resource, NULL, NULL);
}
#endif
if (response_buffer.response_length) {
response->payload_m = response_buffer.buffer;
response->payload_len = OS_MBUF_PKTLEN(response_buffer.buffer);
response_buffer.buffer = NULL; /* freed in coap_serialize_message() */
coap_set_header_content_format(response, APPLICATION_CBOR);
}
/* response_buffer.code at this point contains a valid CoAP status
* code.
*/
coap_set_status_code(response, response_buffer.code);
}
if (response_buffer.buffer) {
os_mbuf_free_chain(response_buffer.buffer);
}
return success;
}
#ifdef OC_CLIENT
static void
free_client_cb(oc_client_cb_t *cb)
{
os_callout_stop(&cb->callout);
oc_free_string(&cb->uri);
SLIST_REMOVE(&oc_client_cbs, cb, oc_client_cb, next);
os_memblock_put(&oc_client_cb_pool, cb);
}
void
oc_ri_remove_client_cb_by_mid(uint16_t mid)
{
oc_client_cb_t *cb;
SLIST_FOREACH(cb, &oc_client_cbs, next) {
if (cb->mid == mid) {
break;
}
}
if (cb) {
free_client_cb(cb);
}
}
bool
oc_ri_send_rst(oc_endpoint_t *endpoint, uint8_t *token, uint8_t token_len,
uint16_t mid)
{
coap_packet_t rst[1];
struct os_mbuf *m;
coap_init_message(rst, COAP_TYPE_RST, 0, mid);
coap_set_header_observe(rst, 1);
coap_set_token(rst, token, token_len);
m = oc_allocate_mbuf(endpoint);
if (m) {
if (!coap_serialize_message(rst, m)) {
coap_send_message(m, 0);
} else {
os_mbuf_free_chain(m);
}
return true;
}
return false;
}
bool
oc_ri_invoke_client_cb(struct coap_packet_rx *rsp, oc_endpoint_t *endpoint)
{
oc_client_cb_t *cb, *tmp;
oc_client_response_t client_response;
unsigned int content_format = APPLICATION_CBOR;
oc_response_handler_t handler;
int i;
/*
if con then send ack and process as above
-empty ack sent from below by engine
if ack with piggyback then process as above
-processed below
if ack and empty then it is a separate response, and keep cb
-handled by separate flag
if ack is for block then store data and pass to client
*/
coap_get_header_content_format(rsp, &content_format);
cb = SLIST_FIRST(&oc_client_cbs);
while (cb != NULL) {
tmp = SLIST_NEXT(cb, next);
if (cb->token_len != rsp->token_len ||
memcmp(cb->token, rsp->token, rsp->token_len)) {
cb = tmp;
continue;
}
/* If content format is not CBOR, then reject response
and clear callback
If incoming response type is RST, then clear callback
*/
if (content_format != APPLICATION_CBOR || rsp->type == COAP_TYPE_RST) {
free_client_cb(cb);
break;
}
/* Check code, translate to oc_status_code, store
Check observe option:
if no observe option, set to -1, else store observe seq
*/
client_response.packet = NULL;
client_response.observe_option = -1;
for (i = 0; i < __NUM_OC_STATUS_CODES__; i++) {
if (oc_coap_status_codes[i] == rsp->code) {
client_response.code = i;
break;
}
}
coap_get_header_observe(rsp, &client_response.observe_option);
bool separate = false;
/*
if payload exists, process payload and save in client response
send client response to callback and return
*/
if (rsp->payload_len) {
if (cb->discovery) {
#if MYNEWT_VAL(OC_CLIENT_DISCOVERY_ENABLE)
if (oc_ri_process_discovery_payload(rsp, cb->handler,
endpoint) == OC_STOP_DISCOVERY) {
free_client_cb(cb);
return true;
}
#else
/* XXX should never be here */
free_client_cb(cb);
return true;
#endif
} else {
client_response.packet = rsp;
client_response.origin = endpoint;
handler = (oc_response_handler_t)cb->handler;
handler(&client_response);
}
} else { // no payload
if (rsp->type == COAP_TYPE_ACK && rsp->code == 0) {
separate = true;
} else if (!cb->discovery) {
handler = (oc_response_handler_t)cb->handler;
handler(&client_response);
}
}
/* check observe sequence number:
if -1 then remove cb, else keep cb
if it is an ACK for a separate response, keep cb
if it is a discovery response, keep cb so that it will last
for the entirety of OC_CLIENT_CB_TIMEOUT_SECS
*/
if (client_response.observe_option == -1 && !separate &&
!cb->discovery) {
free_client_cb(cb);
} else {
cb->observe_seq = client_response.observe_option;
}
break;
}
return true;
}
oc_client_cb_t *
oc_ri_get_client_cb(const char *uri, oc_server_handle_t *server,
oc_method_t method)
{
oc_client_cb_t *cb;
SLIST_FOREACH(cb, &oc_client_cbs, next) {
if (oc_string_len(cb->uri) == strlen(uri) &&
strncmp(oc_string(cb->uri), uri, strlen(uri)) == 0 &&
memcmp(&cb->server.endpoint, &server->endpoint,
oc_endpoint_size(&cb->server.endpoint)) == 0 &&
cb->method == method) {
return cb;
}
}
return NULL;
}
static void
oc_ri_remove_cb(struct os_event *ev)
{
struct oc_client_cb *cb;
cb = ev->ev_arg;
free_client_cb(cb);
}
oc_client_cb_t *
oc_ri_alloc_client_cb(const char *uri, oc_server_handle_t *server,
oc_method_t method, void *handler, oc_qos_t qos)
{
oc_client_cb_t *cb;
cb = os_memblock_get(&oc_client_cb_pool);
if (!cb) {
return NULL;
}
cb->mid = coap_get_mid();
oc_new_string(&cb->uri, uri);
cb->method = method;
cb->qos = qos;
cb->handler = handler;
cb->token_len = 8;
int i = 0;
uint16_t r;
while (i < cb->token_len) {
r = oc_random_rand();
memcpy(cb->token + i, &r, sizeof(r));
i += sizeof(r);
}
cb->discovery = false;
cb->timestamp = oc_clock_time();
cb->observe_seq = -1;
memcpy(&cb->server, server, sizeof(oc_server_handle_t));
os_callout_init(&cb->callout, oc_evq_get(), oc_ri_remove_cb, cb);
SLIST_INSERT_HEAD(&oc_client_cbs, cb, next);
return cb;
}
#endif /* OC_CLIENT */
// TODO:
// resource collections
// if method accepted by interface selection
// resources for presence based discovery