/* * Copyright (c) 2015-2017 Ken Bannister. All rights reserved. * * 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 examples * @{ * * @file * @brief gcoap CLI support * * @author Ken Bannister * @author Hauke Petersen * * @} */ #include #include #include #include #include "fmt.h" #include "net/gcoap.h" #include "net/utils.h" #include "od.h" #include "gcoap_example.h" #define ENABLE_DEBUG 0 #include "debug.h" #if IS_USED(MODULE_GCOAP_DTLS) #include "net/credman.h" #include "net/dsm.h" #include "tinydtls_keys.h" /* Example credential tag for credman. Tag together with the credential type needs to be unique. */ #define GCOAP_DTLS_CREDENTIAL_TAG 10 static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY; static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY; static const credman_credential_t credential = { .type = CREDMAN_TYPE_PSK, .tag = GCOAP_DTLS_CREDENTIAL_TAG, .params = { .psk = { .key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, }, .id = { .s = psk_id_0, .len = sizeof(psk_id_0) - 1, }, } }, }; #endif static ssize_t _encode_link(const coap_resource_t *resource, char *buf, size_t maxlen, coap_link_encoder_ctx_t *context); static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx); static ssize_t _riot_board_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx); /* CoAP resources. Must be sorted by path (ASCII order). */ static const coap_resource_t _resources[] = { { "/cli/stats", COAP_GET | COAP_PUT, _stats_handler, NULL }, { "/riot/board", COAP_GET, _riot_board_handler, NULL }, }; static const char *_link_params[] = { ";ct=0;rt=\"count\";obs", NULL }; static gcoap_listener_t _listener = { &_resources[0], ARRAY_SIZE(_resources), GCOAP_SOCKET_TYPE_UNDEF, _encode_link, NULL, NULL }; /* Adds link format params to resource list */ static ssize_t _encode_link(const coap_resource_t *resource, char *buf, size_t maxlen, coap_link_encoder_ctx_t *context) { ssize_t res = gcoap_encode_link(resource, buf, maxlen, context); if (res > 0) { if (_link_params[context->link_pos] && (strlen(_link_params[context->link_pos]) < (maxlen - res))) { if (buf) { memcpy(buf+res, _link_params[context->link_pos], strlen(_link_params[context->link_pos])); } return res + strlen(_link_params[context->link_pos]); } } return res; } /* * Server callback for /cli/stats. Accepts either a GET or a PUT. * * GET: Returns the count of packets sent by the CLI. * PUT: Updates the count of packets. Rejects an obviously bad request, but * allows any two byte value for example purposes. Semantically, the only * valid action is to set the value to 0. */ static ssize_t _stats_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx) { (void)ctx; /* read coap method type in packet */ unsigned method_flag = coap_method2flag(coap_get_code_detail(pdu)); switch (method_flag) { case COAP_GET: gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); coap_opt_add_format(pdu, COAP_FORMAT_TEXT); size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); /* write the response buffer with the request count value */ resp_len += fmt_u16_dec((char *)pdu->payload, req_count); return resp_len; case COAP_PUT: /* convert the payload to an integer and update the internal value */ if (pdu->payload_len <= 5) { char payload[6] = { 0 }; memcpy(payload, (char *)pdu->payload, pdu->payload_len); req_count = (uint16_t)strtoul(payload, NULL, 10); return gcoap_response(pdu, buf, len, COAP_CODE_CHANGED); } else { return gcoap_response(pdu, buf, len, COAP_CODE_BAD_REQUEST); } } return 0; } static ssize_t _riot_board_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len, coap_request_ctx_t *ctx) { (void)ctx; gcoap_resp_init(pdu, buf, len, COAP_CODE_CONTENT); coap_opt_add_format(pdu, COAP_FORMAT_TEXT); size_t resp_len = coap_opt_finish(pdu, COAP_OPT_FINISH_PAYLOAD); /* write the RIOT board name in the response buffer */ if (pdu->payload_len >= strlen(RIOT_BOARD)) { memcpy(pdu->payload, RIOT_BOARD, strlen(RIOT_BOARD)); return resp_len + strlen(RIOT_BOARD); } else { puts("gcoap_cli: msg buffer too small"); return gcoap_response(pdu, buf, len, COAP_CODE_INTERNAL_SERVER_ERROR); } } void notify_observers(void) { size_t len; uint8_t buf[CONFIG_GCOAP_PDU_BUF_SIZE]; coap_pkt_t pdu; /* send Observe notification for /cli/stats */ switch (gcoap_obs_init(&pdu, &buf[0], CONFIG_GCOAP_PDU_BUF_SIZE, &_resources[0])) { case GCOAP_OBS_INIT_OK: DEBUG("gcoap_cli: creating /cli/stats notification\n"); coap_opt_add_format(&pdu, COAP_FORMAT_TEXT); len = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD); len += fmt_u16_dec((char *)pdu.payload, req_count); gcoap_obs_send(&buf[0], len, &_resources[0]); break; case GCOAP_OBS_INIT_UNUSED: DEBUG("gcoap_cli: no observer for /cli/stats\n"); break; case GCOAP_OBS_INIT_ERR: DEBUG("gcoap_cli: error initializing /cli/stats notification\n"); break; } } void server_init(void) { #if IS_USED(MODULE_GCOAP_DTLS) int res = credman_add(&credential); if (res < 0 && res != CREDMAN_EXIST) { /* ignore duplicate credentials */ printf("gcoap: cannot add credential to system: %d\n", res); return; } sock_dtls_t *gcoap_sock_dtls = gcoap_get_sock_dtls(); res = sock_dtls_add_credential(gcoap_sock_dtls, GCOAP_DTLS_CREDENTIAL_TAG); if (res < 0) { printf("gcoap: cannot add credential to DTLS sock: %d\n", res); } #endif gcoap_register_listener(&_listener); }