|
|
|
@ -34,11 +34,19 @@ static void *_event_loop(void *arg);
|
|
|
|
|
static void _listen(sock_udp_t *sock);
|
|
|
|
|
static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len);
|
|
|
|
|
static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len);
|
|
|
|
|
static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len);
|
|
|
|
|
static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
|
|
|
|
sock_udp_ep_t *remote);
|
|
|
|
|
static ssize_t _finish_pdu(coap_pkt_t *pdu, uint8_t *buf, size_t len);
|
|
|
|
|
static void _expire_request(gcoap_request_memo_t *memo);
|
|
|
|
|
static void _find_req_memo(gcoap_request_memo_t **memo_ptr, coap_pkt_t *pdu,
|
|
|
|
|
uint8_t *buf, size_t len);
|
|
|
|
|
static void _find_resource(coap_pkt_t *pdu, coap_resource_t **resource_ptr,
|
|
|
|
|
gcoap_listener_t **listener_ptr);
|
|
|
|
|
static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote);
|
|
|
|
|
static int _find_obs_memo(gcoap_observe_memo_t **memo, sock_udp_ep_t *remote,
|
|
|
|
|
coap_pkt_t *pdu);
|
|
|
|
|
static void _find_obs_memo_resource(gcoap_observe_memo_t **memo,
|
|
|
|
|
const coap_resource_t *resource);
|
|
|
|
|
|
|
|
|
|
/* Internal variables */
|
|
|
|
|
const coap_resource_t _default_resources[] = {
|
|
|
|
@ -132,7 +140,7 @@ static void _listen(sock_udp_t *sock)
|
|
|
|
|
|
|
|
|
|
/* incoming request */
|
|
|
|
|
if (coap_get_code_class(&pdu) == COAP_CLASS_REQ) {
|
|
|
|
|
size_t pdu_len = _handle_req(&pdu, buf, sizeof(buf));
|
|
|
|
|
size_t pdu_len = _handle_req(&pdu, buf, sizeof(buf), &remote);
|
|
|
|
|
if (pdu_len > 0) {
|
|
|
|
|
sock_udp_send(sock, buf, pdu_len, &remote);
|
|
|
|
|
}
|
|
|
|
@ -152,8 +160,104 @@ static void _listen(sock_udp_t *sock)
|
|
|
|
|
* Main request handler: generates response PDU in the provided buffer.
|
|
|
|
|
*
|
|
|
|
|
* Caller must finish the PDU and send it.
|
|
|
|
|
*
|
|
|
|
|
* return length of response pdu, or < 0 if can't handle
|
|
|
|
|
*/
|
|
|
|
|
static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
|
|
|
|
sock_udp_ep_t *remote)
|
|
|
|
|
{
|
|
|
|
|
coap_resource_t *resource;
|
|
|
|
|
gcoap_listener_t *listener;
|
|
|
|
|
sock_udp_ep_t *observer = NULL;
|
|
|
|
|
gcoap_observe_memo_t *memo = NULL;
|
|
|
|
|
gcoap_observe_memo_t *resource_memo = NULL;
|
|
|
|
|
|
|
|
|
|
_find_resource(pdu, &resource, &listener);
|
|
|
|
|
if (resource == NULL) {
|
|
|
|
|
return gcoap_response(pdu, buf, len, COAP_CODE_PATH_NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* used below to ensure a memo not already recorded for the resource */
|
|
|
|
|
_find_obs_memo_resource(&resource_memo, resource);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (coap_get_observe(pdu) == COAP_OBS_REGISTER) {
|
|
|
|
|
int empty_slot = _find_obs_memo(&memo, remote, pdu);
|
|
|
|
|
/* record observe memo */
|
|
|
|
|
if (memo == NULL) {
|
|
|
|
|
if (empty_slot >= 0 && resource_memo == NULL) {
|
|
|
|
|
|
|
|
|
|
int obs_slot = _find_observer(&observer, remote);
|
|
|
|
|
/* cache new observer */
|
|
|
|
|
if (observer == NULL) {
|
|
|
|
|
if (obs_slot >= 0) {
|
|
|
|
|
observer = &_coap_state.observers[obs_slot];
|
|
|
|
|
memcpy(observer, remote, sizeof(sock_udp_ep_t));
|
|
|
|
|
} else {
|
|
|
|
|
DEBUG("gcoap: can't register observer\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (observer != NULL) {
|
|
|
|
|
memo = &_coap_state.observe_memos[empty_slot];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (memo == NULL) {
|
|
|
|
|
coap_clear_observe(pdu);
|
|
|
|
|
DEBUG("gcoap: can't register observe memo\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (memo != NULL) {
|
|
|
|
|
memo->observer = observer;
|
|
|
|
|
memo->resource = resource;
|
|
|
|
|
memo->token_len = coap_get_token_len(pdu);
|
|
|
|
|
if (memo->token_len) {
|
|
|
|
|
memcpy(&memo->token[0], pdu->token, memo->token_len);
|
|
|
|
|
}
|
|
|
|
|
DEBUG("gcoap: Registered observer for: %s\n", memo->resource->path);
|
|
|
|
|
/* generate initial notification value */
|
|
|
|
|
uint32_t now = xtimer_now_usec();
|
|
|
|
|
pdu->observe_value = (now >> GCOAP_OBS_TICK_EXPONENT) & 0xFFFFFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (coap_get_observe(pdu) == COAP_OBS_DEREGISTER) {
|
|
|
|
|
_find_obs_memo(&memo, remote, pdu);
|
|
|
|
|
/* clear memo, and clear observer if no other memos */
|
|
|
|
|
if (memo != NULL) {
|
|
|
|
|
DEBUG("gcoap: Deregistering observer for: %s\n", memo->resource->path);
|
|
|
|
|
memo->observer = NULL;
|
|
|
|
|
memo = NULL;
|
|
|
|
|
_find_obs_memo(&memo, remote, NULL);
|
|
|
|
|
if (memo == NULL) {
|
|
|
|
|
_find_observer(&observer, remote);
|
|
|
|
|
if (observer != NULL) {
|
|
|
|
|
observer->family = AF_UNSPEC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
coap_clear_observe(pdu);
|
|
|
|
|
|
|
|
|
|
} else if (coap_has_observe(pdu)) {
|
|
|
|
|
/* bogus request; don't respond */
|
|
|
|
|
DEBUG("gcoap: Observe value unexpected: %" PRIu32 "\n", coap_get_observe(pdu));
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t pdu_len = resource->handler(pdu, buf, len);
|
|
|
|
|
if (pdu_len < 0) {
|
|
|
|
|
pdu_len = gcoap_response(pdu, buf, len,
|
|
|
|
|
COAP_CODE_INTERNAL_SERVER_ERROR);
|
|
|
|
|
}
|
|
|
|
|
return pdu_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Searches listener registrations for the resource matching the path in a PDU.
|
|
|
|
|
*
|
|
|
|
|
* param[out] resource_ptr -- found resource
|
|
|
|
|
* param[out] listener_ptr -- listener for found resource
|
|
|
|
|
*/
|
|
|
|
|
static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len)
|
|
|
|
|
static void _find_resource(coap_pkt_t *pdu, coap_resource_t **resource_ptr,
|
|
|
|
|
gcoap_listener_t **listener_ptr)
|
|
|
|
|
{
|
|
|
|
|
unsigned method_flag = coap_method2flag(coap_get_code_detail(pdu));
|
|
|
|
|
|
|
|
|
@ -178,18 +282,16 @@ static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ssize_t pdu_len = resource->handler(pdu, buf, len);
|
|
|
|
|
if (pdu_len < 0) {
|
|
|
|
|
pdu_len = gcoap_response(pdu, buf, len,
|
|
|
|
|
COAP_CODE_INTERNAL_SERVER_ERROR);
|
|
|
|
|
}
|
|
|
|
|
return pdu_len;
|
|
|
|
|
*resource_ptr = resource;
|
|
|
|
|
*listener_ptr = listener;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
listener = listener->next;
|
|
|
|
|
}
|
|
|
|
|
/* resource not found */
|
|
|
|
|
return gcoap_response(pdu, buf, len, COAP_CODE_PATH_NOT_FOUND);
|
|
|
|
|
*resource_ptr = NULL;
|
|
|
|
|
*listener_ptr = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@ -329,6 +431,22 @@ static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len)
|
|
|
|
|
|
|
|
|
|
uint8_t *bufpos = buf + coap_get_total_hdr_len(pdu); /* position for write */
|
|
|
|
|
|
|
|
|
|
/* Observe for notification or registration response */
|
|
|
|
|
if (coap_get_code_class(pdu) == COAP_CLASS_SUCCESS && coap_has_observe(pdu)) {
|
|
|
|
|
uint32_t nval = htonl(pdu->observe_value);
|
|
|
|
|
uint8_t *nbyte = (uint8_t *)&nval;
|
|
|
|
|
unsigned i;
|
|
|
|
|
/* find address of non-zero MSB; max 3 bytes */
|
|
|
|
|
for (i = 1; i < 4; i++) {
|
|
|
|
|
if (*(nbyte+i) > 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bufpos += coap_put_option(bufpos, last_optnum, COAP_OPT_OBSERVE,
|
|
|
|
|
nbyte+i, 4-i);
|
|
|
|
|
last_optnum = COAP_OPT_OBSERVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Uri-Path for request */
|
|
|
|
|
if (coap_get_code_class(pdu) == COAP_CLASS_REQ) {
|
|
|
|
|
size_t url_len = strlen((char *)pdu->url);
|
|
|
|
@ -355,6 +473,108 @@ static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len)
|
|
|
|
|
return bufpos - buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find registered observer for a remote address and port.
|
|
|
|
|
*
|
|
|
|
|
* observer[out] -- Registered observer, or NULL if not found
|
|
|
|
|
* remote[in] -- Endpoint to match
|
|
|
|
|
*
|
|
|
|
|
* return Index of empty slot, suitable for registering new observer; or -1
|
|
|
|
|
* if no empty slots. Undefined if observer found.
|
|
|
|
|
*/
|
|
|
|
|
static int _find_observer(sock_udp_ep_t **observer, sock_udp_ep_t *remote)
|
|
|
|
|
{
|
|
|
|
|
int empty_slot = -1;
|
|
|
|
|
*observer = NULL;
|
|
|
|
|
for (unsigned i = 0; i < GCOAP_OBS_CLIENTS_MAX; i++) {
|
|
|
|
|
unsigned cmplen = 0;
|
|
|
|
|
|
|
|
|
|
if (_coap_state.observers[i].family == AF_UNSPEC) {
|
|
|
|
|
cmplen = 0;
|
|
|
|
|
empty_slot = i;
|
|
|
|
|
}
|
|
|
|
|
else if (_coap_state.observers[i].family == AF_INET6) {
|
|
|
|
|
cmplen = 16;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
cmplen = 4;
|
|
|
|
|
}
|
|
|
|
|
if (cmplen &&
|
|
|
|
|
memcmp(&_coap_state.observers[i].addr.ipv6[0], &remote->addr.ipv6[0],
|
|
|
|
|
cmplen) == 0
|
|
|
|
|
&& _coap_state.observers[i].port == remote->port) {
|
|
|
|
|
|
|
|
|
|
*observer = &_coap_state.observers[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return empty_slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find registered observe memo for a remote address and token.
|
|
|
|
|
*
|
|
|
|
|
* memo[out] -- Registered observe memo, or NULL if not found
|
|
|
|
|
* remote[in] -- Endpoint for address to match
|
|
|
|
|
* pdu[in] -- PDU for token to match, or NULL to match only on remote address
|
|
|
|
|
*
|
|
|
|
|
* return Index of empty slot, suitable for registering new memo; or -1 if no
|
|
|
|
|
* empty slots. Undefined if memo found.
|
|
|
|
|
*/
|
|
|
|
|
static int _find_obs_memo(gcoap_observe_memo_t **memo, sock_udp_ep_t *remote,
|
|
|
|
|
coap_pkt_t *pdu)
|
|
|
|
|
{
|
|
|
|
|
int empty_slot = -1;
|
|
|
|
|
*memo = NULL;
|
|
|
|
|
|
|
|
|
|
sock_udp_ep_t *remote_observer = NULL;
|
|
|
|
|
_find_observer(&remote_observer, remote);
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < GCOAP_OBS_REGISTRATIONS_MAX; i++) {
|
|
|
|
|
if (_coap_state.observe_memos[i].observer == NULL) {
|
|
|
|
|
empty_slot = i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_coap_state.observe_memos[i].observer == remote_observer) {
|
|
|
|
|
if (pdu == NULL) {
|
|
|
|
|
*memo = &_coap_state.observe_memos[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_coap_state.observe_memos[i].token_len == coap_get_token_len(pdu)) {
|
|
|
|
|
unsigned cmplen = _coap_state.observe_memos[i].token_len;
|
|
|
|
|
if (cmplen &&
|
|
|
|
|
memcmp(&_coap_state.observe_memos[i].token[0], &pdu->token[0],
|
|
|
|
|
cmplen) == 0) {
|
|
|
|
|
*memo = &_coap_state.observe_memos[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return empty_slot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Find registered observe memo for a resource.
|
|
|
|
|
*
|
|
|
|
|
* memo[out] -- Registered observe memo, or NULL if not found
|
|
|
|
|
* resource[in] -- Resource to match
|
|
|
|
|
*/
|
|
|
|
|
static void _find_obs_memo_resource(gcoap_observe_memo_t **memo,
|
|
|
|
|
const coap_resource_t *resource)
|
|
|
|
|
{
|
|
|
|
|
*memo = NULL;
|
|
|
|
|
for (int i = 0; i < GCOAP_OBS_REGISTRATIONS_MAX; i++) {
|
|
|
|
|
if (_coap_state.observe_memos[i].observer != NULL
|
|
|
|
|
&& _coap_state.observe_memos[i].resource == resource) {
|
|
|
|
|
*memo = &_coap_state.observe_memos[i];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* gcoap interface functions
|
|
|
|
|
*/
|
|
|
|
@ -367,8 +587,10 @@ kernel_pid_t gcoap_init(void)
|
|
|
|
|
_pid = thread_create(_msg_stack, sizeof(_msg_stack), THREAD_PRIORITY_MAIN - 1,
|
|
|
|
|
THREAD_CREATE_STACKTEST, _event_loop, NULL, "coap");
|
|
|
|
|
|
|
|
|
|
/* Blank list of open requests so we know if an entry is available. */
|
|
|
|
|
/* Blank lists so we know if an entry is available. */
|
|
|
|
|
memset(&_coap_state.open_reqs[0], 0, sizeof(_coap_state.open_reqs));
|
|
|
|
|
memset(&_coap_state.observers[0], 0, sizeof(_coap_state.observers));
|
|
|
|
|
memset(&_coap_state.observe_memos[0], 0, sizeof(_coap_state.observe_memos));
|
|
|
|
|
/* randomize initial value */
|
|
|
|
|
_coap_state.last_message_id = random_uint32() & 0xFFFF;
|
|
|
|
|
|
|
|
|
@ -518,6 +740,55 @@ int gcoap_resp_init(coap_pkt_t *pdu, uint8_t *buf, size_t len, unsigned code)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int gcoap_obs_init(coap_pkt_t *pdu, uint8_t *buf, size_t len,
|
|
|
|
|
const coap_resource_t *resource)
|
|
|
|
|
{
|
|
|
|
|
ssize_t hdrlen;
|
|
|
|
|
gcoap_observe_memo_t *memo = NULL;
|
|
|
|
|
|
|
|
|
|
_find_obs_memo_resource(&memo, resource);
|
|
|
|
|
if (memo == NULL) {
|
|
|
|
|
/* Unique return value to specify there is not an observer */
|
|
|
|
|
return GCOAP_OBS_INIT_UNUSED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pdu->hdr = (coap_hdr_t *)buf;
|
|
|
|
|
hdrlen = coap_build_hdr(pdu->hdr, COAP_TYPE_NON, &memo->token[0],
|
|
|
|
|
memo->token_len, COAP_CODE_CONTENT,
|
|
|
|
|
++_coap_state.last_message_id);
|
|
|
|
|
if (hdrlen > 0) {
|
|
|
|
|
uint32_t now = xtimer_now_usec();
|
|
|
|
|
pdu->observe_value = (now >> GCOAP_OBS_TICK_EXPONENT) & 0xFFFFFF;
|
|
|
|
|
|
|
|
|
|
/* Reserve some space between the header and payload to write options later */
|
|
|
|
|
pdu->payload = buf + coap_get_total_hdr_len(pdu) + GCOAP_OBS_OPTIONS_BUF;
|
|
|
|
|
/* Payload length really zero at this point, but we set this to the available
|
|
|
|
|
* length in the buffer. Allows us to reconstruct buffer length later. */
|
|
|
|
|
pdu->payload_len = len - (pdu->payload - buf);
|
|
|
|
|
pdu->content_type = COAP_FORMAT_NONE;
|
|
|
|
|
|
|
|
|
|
return GCOAP_OBS_INIT_OK;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* reason for negative hdrlen is not defined, so we also are vague */
|
|
|
|
|
return GCOAP_OBS_INIT_ERR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t gcoap_obs_send(uint8_t *buf, size_t len, const coap_resource_t *resource)
|
|
|
|
|
{
|
|
|
|
|
gcoap_observe_memo_t *memo = NULL;
|
|
|
|
|
|
|
|
|
|
_find_obs_memo_resource(&memo, resource);
|
|
|
|
|
|
|
|
|
|
if (memo) {
|
|
|
|
|
return sock_udp_send(&_sock, buf, len, memo->observer);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t gcoap_op_state(void)
|
|
|
|
|
{
|
|
|
|
|
uint8_t count = 0;
|
|
|
|
|