mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
364 lines
9.1 KiB
C
364 lines
9.1 KiB
C
/*
|
|
* Copyright (C) 2017-2018 Freie Universität Berlin
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup net_rdcli
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief CoRE Resource Directory client implementation
|
|
*
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "fmt.h"
|
|
#include "mutex.h"
|
|
#include "thread_flags.h"
|
|
|
|
#include "net/gcoap.h"
|
|
#include "net/ipv6/addr.h"
|
|
#include "net/rdcli.h"
|
|
#include "net/rdcli_common.h"
|
|
#include "net/rdcli_config.h"
|
|
|
|
#ifdef MODULE_RDCLI_STANDALONE
|
|
#include "net/rdcli_standalone.h"
|
|
#endif
|
|
|
|
#define ENABLE_DEBUG (0)
|
|
#include "debug.h"
|
|
|
|
#define FLAG_SUCCESS (0x0001)
|
|
#define FLAG_TIMEOUT (0x0002)
|
|
#define FLAG_ERR (0x0004)
|
|
#define FLAG_OVERFLOW (0x0008)
|
|
#define FLAG_MASK (0x000f)
|
|
|
|
#define BUFSIZE (512U)
|
|
|
|
static char *_regif_buf;
|
|
static size_t _regif_buf_len;
|
|
|
|
static char _rd_loc[NANOCOAP_URI_MAX];
|
|
static char _rd_regif[NANOCOAP_URI_MAX];
|
|
static sock_udp_ep_t _rd_remote;
|
|
|
|
static mutex_t _mutex = MUTEX_INIT;
|
|
static volatile thread_t *_waiter;
|
|
|
|
static uint8_t buf[BUFSIZE];
|
|
|
|
static void _lock(void)
|
|
{
|
|
mutex_lock(&_mutex);
|
|
_waiter = sched_active_thread;
|
|
}
|
|
|
|
static int _sync(void)
|
|
{
|
|
thread_flags_t flags = thread_flags_wait_any(FLAG_MASK);
|
|
|
|
if (flags & FLAG_ERR) {
|
|
return RDCLI_ERR;
|
|
}
|
|
else if (flags & FLAG_TIMEOUT) {
|
|
return RDCLI_TIMEOUT;
|
|
}
|
|
else if (flags & FLAG_OVERFLOW) {
|
|
return RDCLI_OVERFLOW;
|
|
}
|
|
else {
|
|
return RDCLI_OK;
|
|
}
|
|
}
|
|
|
|
static void _on_register(unsigned req_state, coap_pkt_t* pdu,
|
|
sock_udp_ep_t *remote)
|
|
{
|
|
thread_flags_t flag = FLAG_ERR;
|
|
|
|
if ((req_state == GCOAP_MEMO_RESP) &&
|
|
(pdu->hdr->code == COAP_CODE_CREATED)) {
|
|
/* read the location header and save the RD details on success */
|
|
if (coap_get_location_path(pdu, (uint8_t *)_rd_loc,
|
|
sizeof(_rd_loc)) > 0) {
|
|
memcpy(&_rd_remote, remote, sizeof(_rd_remote));
|
|
flag = FLAG_SUCCESS;
|
|
}
|
|
else {
|
|
/* reset RD entry */
|
|
flag = FLAG_OVERFLOW;
|
|
}
|
|
}
|
|
else if (req_state == GCOAP_MEMO_TIMEOUT) {
|
|
flag = FLAG_TIMEOUT;
|
|
}
|
|
|
|
thread_flags_set((thread_t *)_waiter, flag);
|
|
}
|
|
|
|
static void _on_update_remove(unsigned req_state, coap_pkt_t *pdu, uint8_t code)
|
|
{
|
|
thread_flags_t flag = FLAG_ERR;
|
|
|
|
if ((req_state == GCOAP_MEMO_RESP) && (pdu->hdr->code == code)) {
|
|
flag = FLAG_SUCCESS;
|
|
}
|
|
else if (req_state == GCOAP_MEMO_TIMEOUT) {
|
|
flag = FLAG_TIMEOUT;
|
|
}
|
|
|
|
thread_flags_set((thread_t *)_waiter, flag);
|
|
}
|
|
|
|
static void _on_update(unsigned req_state, coap_pkt_t *pdu, sock_udp_ep_t *remote)
|
|
{
|
|
(void)remote;
|
|
_on_update_remove(req_state, pdu, COAP_CODE_CHANGED);
|
|
}
|
|
|
|
static void _on_remove(unsigned req_state, coap_pkt_t *pdu, sock_udp_ep_t *remote)
|
|
{
|
|
(void)remote;
|
|
_on_update_remove(req_state, pdu, COAP_CODE_DELETED);
|
|
}
|
|
|
|
static int _update_remove(unsigned code, gcoap_resp_handler_t handle)
|
|
{
|
|
coap_pkt_t pkt;
|
|
|
|
if (_rd_loc[0] == 0) {
|
|
return RDCLI_NORD;
|
|
}
|
|
|
|
/* build CoAP request packet */
|
|
int res = gcoap_req_init(&pkt, buf, sizeof(buf), code, _rd_loc);
|
|
if (res < 0) {
|
|
return RDCLI_ERR;
|
|
}
|
|
coap_hdr_set_type(pkt.hdr, COAP_TYPE_CON);
|
|
ssize_t pkt_len = gcoap_finish(&pkt, 0, COAP_FORMAT_NONE);
|
|
|
|
/* send request */
|
|
gcoap_req_send2(buf, pkt_len, &_rd_remote, handle);
|
|
|
|
/* synchronize response */
|
|
return _sync();
|
|
}
|
|
|
|
static void _on_discover(unsigned req_state, coap_pkt_t *pdu,
|
|
sock_udp_ep_t *remote)
|
|
{
|
|
thread_flags_t flag = RDCLI_NORD;
|
|
(void)remote;
|
|
|
|
if (req_state == GCOAP_MEMO_RESP) {
|
|
unsigned ct = coap_get_content_type(pdu);
|
|
if (ct != COAP_FORMAT_LINK) {
|
|
goto end;
|
|
}
|
|
if (pdu->payload_len == 0) {
|
|
goto end;
|
|
}
|
|
/* do simplified parsing of registration interface location */
|
|
char *start = (char *)pdu->payload;
|
|
char *end;
|
|
char *limit = (char *)(pdu->payload + pdu->payload_len);
|
|
while ((*start != '<') && (start < limit)) {
|
|
start++;
|
|
}
|
|
if (*start != '<') {
|
|
goto end;
|
|
}
|
|
end = ++start;
|
|
while ((*end != '>') && (end < limit)) {
|
|
end++;
|
|
}
|
|
if (*end != '>') {
|
|
goto end;
|
|
}
|
|
/* TODO: verify size of interface resource identifier */
|
|
size_t uri_len = (size_t)(end - start);
|
|
if (uri_len >= _regif_buf_len) {
|
|
goto end;
|
|
}
|
|
memcpy(_regif_buf, start, uri_len);
|
|
memset((_regif_buf + uri_len), 0, (_regif_buf_len - uri_len));
|
|
flag = FLAG_SUCCESS;
|
|
}
|
|
else if (req_state == GCOAP_MEMO_TIMEOUT) {
|
|
flag = FLAG_TIMEOUT;
|
|
}
|
|
|
|
end:
|
|
thread_flags_set((thread_t *)_waiter, flag);
|
|
}
|
|
|
|
static int _discover_internal(const sock_udp_ep_t *remote,
|
|
char *regif, size_t maxlen)
|
|
{
|
|
coap_pkt_t pkt;
|
|
|
|
/* save pointer to result buffer */
|
|
_regif_buf = regif;
|
|
_regif_buf_len = maxlen;
|
|
|
|
/* do URI discovery for the registration interface */
|
|
int res = gcoap_req_init(&pkt, buf, sizeof(buf), COAP_METHOD_GET,
|
|
"/.well-known/core");
|
|
if (res < 0) {
|
|
return RDCLI_ERR;
|
|
}
|
|
coap_hdr_set_type(pkt.hdr, COAP_TYPE_CON);
|
|
gcoap_add_qstring(&pkt, "rt", "core.rd");
|
|
size_t pkt_len = gcoap_finish(&pkt, 0, COAP_FORMAT_NONE);
|
|
res = gcoap_req_send2(buf, pkt_len, remote, _on_discover);
|
|
if (res < 0) {
|
|
return RDCLI_ERR;
|
|
}
|
|
return _sync();
|
|
}
|
|
|
|
int rdcli_discover_regif(const sock_udp_ep_t *remote, char *regif, size_t maxlen)
|
|
{
|
|
assert(remote && regif);
|
|
|
|
_lock();
|
|
int res = _discover_internal(remote, regif, maxlen);
|
|
mutex_unlock(&_mutex);
|
|
return res;
|
|
}
|
|
|
|
int rdcli_register(const sock_udp_ep_t *remote, const char *regif)
|
|
{
|
|
assert(remote);
|
|
|
|
int res;
|
|
ssize_t pkt_len;
|
|
int retval;
|
|
coap_pkt_t pkt;
|
|
|
|
_lock();
|
|
|
|
/* if no registration interface is given, we will need to trigger a URI
|
|
* discovery for it first (see section 5.2) */
|
|
if (regif == NULL) {
|
|
retval = _discover_internal(remote, _rd_regif, sizeof(_rd_regif));
|
|
if (retval != RDCLI_OK) {
|
|
goto end;
|
|
}
|
|
}
|
|
else {
|
|
if (strlen(_rd_regif) >= sizeof(_rd_regif)) {
|
|
retval = RDCLI_OVERFLOW;
|
|
goto end;
|
|
}
|
|
strncpy(_rd_regif, regif, sizeof(_rd_regif));
|
|
}
|
|
|
|
/* build and send CoAP POST request to the RD's registration interface */
|
|
res = gcoap_req_init(&pkt, buf, sizeof(buf), COAP_METHOD_POST, _rd_regif);
|
|
if (res < 0) {
|
|
retval = RDCLI_ERR;
|
|
goto end;
|
|
}
|
|
/* set some packet options and write query string */
|
|
coap_hdr_set_type(pkt.hdr, COAP_TYPE_CON);
|
|
rdcli_common_add_qstring(&pkt);
|
|
|
|
/* add the resource description as payload */
|
|
res = gcoap_get_resource_list(pkt.payload, pkt.payload_len,
|
|
COAP_FORMAT_LINK);
|
|
if (res < 0) {
|
|
retval = RDCLI_ERR;
|
|
goto end;
|
|
}
|
|
|
|
/* finish up the packet */
|
|
pkt_len = gcoap_finish(&pkt, res, COAP_FORMAT_LINK);
|
|
|
|
/* send out the request */
|
|
res = gcoap_req_send2(buf, pkt_len, remote, _on_register);
|
|
if (res < 0) {
|
|
retval = RDCLI_ERR;
|
|
goto end;
|
|
}
|
|
retval = _sync();
|
|
|
|
end:
|
|
/* if we encountered any error, we mark the client as not connected */
|
|
if (retval != RDCLI_OK) {
|
|
_rd_loc[0] = '\0';
|
|
}
|
|
#ifdef MODULE_RDCLI_STANDALONE
|
|
else {
|
|
rdcli_standalone_signal(true);
|
|
}
|
|
#endif
|
|
|
|
mutex_unlock(&_mutex);
|
|
return retval;
|
|
}
|
|
|
|
int rdcli_update(void)
|
|
{
|
|
_lock();
|
|
int res = _update_remove(COAP_METHOD_POST, _on_update);
|
|
if (res != RDCLI_OK) {
|
|
/* in case we are not able to reach the RD, we drop the association */
|
|
#ifdef MODULE_RDCLI_STANDALONE
|
|
rdcli_standalone_signal(false);
|
|
#endif
|
|
_rd_loc[0] = '\0';
|
|
}
|
|
mutex_unlock(&_mutex);
|
|
return res;
|
|
}
|
|
|
|
int rdcli_remove(void)
|
|
{
|
|
_lock();
|
|
if (_rd_loc[0] == '\0') {
|
|
mutex_unlock(&_mutex);
|
|
return RDCLI_NORD;
|
|
}
|
|
#ifdef MODULE_RDCLI_STANDALONE
|
|
rdcli_standalone_signal(false);
|
|
#endif
|
|
_update_remove(COAP_METHOD_DELETE, _on_remove);
|
|
/* we actually do not care about the result, we drop the RD local RD entry
|
|
* in any case */
|
|
_rd_loc[0] = '\0';
|
|
mutex_unlock(&_mutex);
|
|
return RDCLI_OK;
|
|
}
|
|
|
|
void rdcli_dump_status(void)
|
|
{
|
|
puts("CoAP RD connection status:");
|
|
|
|
if (_rd_loc[0] == 0) {
|
|
puts(" --- not registered with any RD ---");
|
|
}
|
|
else {
|
|
/* get address string */
|
|
char addr[IPV6_ADDR_MAX_STR_LEN];
|
|
ipv6_addr_to_str(addr, (ipv6_addr_t *)&_rd_remote.addr, sizeof(addr));
|
|
|
|
printf("RD address: coap://[%s]:%i\n", addr, (int)_rd_remote.port);
|
|
printf(" ep name: %s\n", rdcli_common_get_ep());
|
|
printf(" lifetime: %is\n", (int)RDCLI_LT);
|
|
printf(" reg if: %s\n", _rd_regif);
|
|
printf(" location: %s\n", _rd_loc);
|
|
}
|
|
}
|