1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

sys: net: add nanocoap

This commit is contained in:
Kaspar Schleiser 2017-11-22 12:28:11 +01:00
parent 9fe9d1fed2
commit 16bdbe5d32
6 changed files with 1202 additions and 0 deletions

View File

@ -121,6 +121,9 @@ endif
ifneq (,$(filter l2filter,$(USEMODULE)))
DIRS += net/link_layer/l2filter
endif
ifneq (,$(filter nanocoap,$(USEMODULE)))
DIRS += net/application_layer/nanocoap
endif
DIRS += $(dir $(wildcard $(addsuffix /Makefile, ${USEMODULE})))

601
sys/include/net/nanocoap.h Normal file
View File

@ -0,0 +1,601 @@
/*
* Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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.
*/
/**
* @defgroup sys_net_nanocoap nanocoap small CoAP library
* @ingroup sys_net
* @brief Provides CoAP functionality optimized for minimal resource usage
*
* @{
*
* @file
* @brief nanocoap API
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef NET_NANOCOAP_H
#define NET_NANOCOAP_H
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <unistd.h>
#ifdef RIOT_VERSION
#include "byteorder.h"
#else
#include <arpa/inet.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief CoAP port to use
*/
#define COAP_PORT (5683)
/**
* @name Nanocoap specific maximum values
* @{
*/
#define NANOCOAP_URL_MAX (64)
#define NANOCOAP_QS_MAX (64)
/** @} */
/**
* @name CoAP option numbers
* @{
*/
#define COAP_OPT_URI_HOST (3)
#define COAP_OPT_OBSERVE (6)
#define COAP_OPT_URI_PATH (11)
#define COAP_OPT_CONTENT_FORMAT (12)
#define COAP_OPT_URI_QUERY (15)
/** @} */
/**
* @name CoAP packet types
* @{
*/
#define COAP_REQ (0)
#define COAP_RESP (2)
#define COAP_RST (3)
/**
* @name Message types -- confirmable, non-confirmable, etc.
* @{
*/
#define COAP_TYPE_CON (0)
#define COAP_TYPE_NON (1)
#define COAP_TYPE_ACK (2)
#define COAP_TYPE_RST (3)
/** @} */
/**
* @name CoAP method codes used in header
* @{
*/
#define COAP_CLASS_REQ (0)
#define COAP_METHOD_GET (1)
#define COAP_METHOD_POST (2)
#define COAP_METHOD_PUT (3)
#define COAP_METHOD_DELETE (4)
/** @} */
/**
* @name CoAP method flags used in coap_handlers array
* @{
*/
#define COAP_GET (0x1)
#define COAP_POST (0x2)
#define COAP_PUT (0x4)
#define COAP_DELETE (0x8)
/** @} */
/**
* @name Empty CoAP message code
* @{
*/
#define COAP_CODE_EMPTY (0)
/** @} */
/**
* @name Response message codes: success
* @{
*/
#define COAP_CLASS_SUCCESS (2)
#define COAP_CODE_CREATED ((2 << 5) | 1)
#define COAP_CODE_DELETED ((2 << 5) | 2)
#define COAP_CODE_VALID ((2 << 5) | 3)
#define COAP_CODE_CHANGED ((2 << 5) | 4)
#define COAP_CODE_204 ((2 << 5) | 4)
#define COAP_CODE_CONTENT ((2 << 5) | 5)
#define COAP_CODE_205 ((2 << 5) | 5)
#define COAP_CODE_231 ((2 << 5) | 31)
/** @} */
/**
* @name Response message codes: client error
* @{
*/
#define COAP_CLASS_CLIENT_FAILURE (4)
#define COAP_CODE_BAD_REQUEST ((4 << 5) | 0)
#define COAP_CODE_UNAUTHORIZED ((4 << 5) | 1)
#define COAP_CODE_BAD_OPTION ((4 << 5) | 2)
#define COAP_CODE_FORBIDDEN ((4 << 5) | 3)
#define COAP_CODE_PATH_NOT_FOUND ((4 << 5) | 4)
#define COAP_CODE_404 ((4 << 5) | 4)
#define COAP_CODE_METHOD_NOT_ALLOWED ((4 << 5) | 5)
#define COAP_CODE_NOT_ACCEPTABLE ((4 << 5) | 6)
#define COAP_CODE_PRECONDITION_FAILED ((4 << 5) | 0xC)
#define COAP_CODE_REQUEST_ENTITY_TOO_LARGE ((4 << 5) | 0xD)
#define COAP_CODE_UNSUPPORTED_CONTENT_FORMAT ((4 << 5) | 0xF)
/** @} */
/**
* @name Response message codes: server error
* @{
*/
#define COAP_CLASS_SERVER_FAILURE (5)
#define COAP_CODE_INTERNAL_SERVER_ERROR ((5 << 5) | 0)
#define COAP_CODE_NOT_IMPLEMENTED ((5 << 5) | 1)
#define COAP_CODE_BAD_GATEWAY ((5 << 5) | 2)
#define COAP_CODE_SERVICE_UNAVAILABLE ((5 << 5) | 3)
#define COAP_CODE_GATEWAY_TIMEOUT ((5 << 5) | 4)
#define COAP_CODE_PROXYING_NOT_SUPPORTED ((5 << 5) | 5)
/** @} */
/**
* @name Content types
* @{
*/
#define COAP_CT_LINK_FORMAT (40)
#define COAP_CT_XML (41)
#define COAP_CT_OCTET_STREAM (42)
#define COAP_CT_EXI (47)
#define COAP_CT_JSON (50)
/** @} */
/**
* @name Content-Format option codes
* @{
*/
#define COAP_FORMAT_TEXT (0)
#define COAP_FORMAT_LINK (40)
#define COAP_FORMAT_OCTET (42)
#define COAP_FORMAT_JSON (50)
#define COAP_FORMAT_CBOR (60)
/** @brief nanocoap-specific value to indicate no format specified. */
#define COAP_FORMAT_NONE (65535)
/** @} */
/**
* @name Observe (RFC 7641) constants
* @{
*/
#define COAP_OBS_REGISTER (0)
#define COAP_OBS_DEREGISTER (1)
/** @} */
/**
* @name Timing parameters
* @{
*/
#define COAP_ACK_TIMEOUT (2U)
#define COAP_RANDOM_FACTOR (1.5)
#define COAP_MAX_RETRANSMIT (4)
#define COAP_NSTART (1)
#define COAP_DEFAULT_LEISURE (5)
/** @} */
/**
* @brief Raw CoAP PDU header structure
*/
typedef struct {
uint8_t ver_t_tkl; /**< version, token, token length */
uint8_t code; /**< CoAP code (e.g.m 205) */
uint16_t id; /**< Req/resp ID */
uint8_t data[]; /**< convenience pointer to payload start */
} coap_hdr_t;
/**
* @brief CoAP option array entry
*/
typedef struct {
coap_hdr_t *hdr; /**< pointer to raw packet */
uint8_t url[NANOCOAP_URL_MAX]; /**< parsed request URL */
uint8_t qs[NANOCOAP_QS_MAX]; /**< parsed query string */
uint8_t *token; /**< pointer to token */
uint8_t *payload; /**< pointer to payload */
unsigned payload_len; /**< length of payload */
uint16_t content_type; /**< content type */
uint32_t observe_value; /**< observe value */
} coap_pkt_t;
/**
* @brief Resource handler type
*/
typedef ssize_t (*coap_handler_t)(coap_pkt_t *pkt, uint8_t *buf, size_t len);
/**
* @brief Type for CoAP resource entry
*/
typedef struct {
const char *path; /**< URI path of resource */
unsigned methods; /**< OR'ed methods this resource allows */
coap_handler_t handler; /**< ptr to resource handler */
} coap_resource_t;
/**
* @brief Global CoAP resource list
*/
extern const coap_resource_t coap_resources[];
/**
* @brief Number of entries in global CoAP resource list
*/
extern const unsigned coap_resources_numof;
/**
* @brief Parse a CoAP PDU
*
* This function parses a raw CoAP PDU from @p buf with size @p len and fills
* the structure pointed to by @p pkt.
* @p pkt must point to a preallocated coap_pkt_t structure.
*
* @param[out] pkt structure to parse into
* @param[in] buf pointer to raw packet data
* @param[in] len length of packet at @p buf
*
* @returns 0 on success
* @returns <0 on error
*/
int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len);
/**
* @brief Build reply to CoAP request
*
* This function can be used to create a reply to any CoAP request packet. It
* will create the reply packet header based on parameters from the request
* (e.g., id, token). Passing a non-zero @p payload_len will ensure the payload
* fits into the buffer along with the header.
*
* @param[in] pkt packet to reply to
* @param[in] code reply code (e.g., COAP_CODE_204)
* @param[out] rbuf buffer to write reply to
* @param[in] rlen size of @p rbuf
* @param[in] payload_len length of payload
*
* @returns size of reply packet on success
* @returns <0 on error
*/
ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
uint8_t *rbuf, unsigned rlen, unsigned payload_len);
/**
* @brief Create CoAP reply (convenience function)
*
* This is a simple wrapper that allows for building CoAP replies for simple
* use-cases.
*
* The reply will be written to @p buf. Is @p payload and @p payload_len
* non-zero, the payload will be copied into the resulting reply packet.
*
* @param[in] pkt packet to reply to
* @param[in] code reply code (e.g., COAP_CODE_204)
* @param[out] buf buffer to write reply to
* @param[in] len size of @p buf
* @param[in] ct content type of payload
* @param[in] payload ptr to payload
* @param[in] payload_len length of payload
*
* @returns size of reply packet on success
* @returns <0 on error
*/
ssize_t coap_reply_simple(coap_pkt_t *pkt,
unsigned code,
uint8_t *buf, size_t len,
unsigned ct,
const uint8_t *payload, uint8_t payload_len);
/**
* @brief Handle incoming CoAP request
*
* This function will find the correct handler, call it and write the reply
* into @p resp_buf.
*
* @param[in] pkt pointer to (parsed) CoAP packet
* @param[out] resp_buf buffer for response
* @param[in] resp_buf_len size of response buffer
*
* @returns size of reply packet on success
* @returns <0 on error
*/
ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_len);
/**
* @brief Builds a CoAP header
*
* Caller *must* ensure @p hdr can hold the header and the full token!
*
* @param[out] hdr hdr to fill
* @param[in] type CoAP packet type (e.g., COAP_TYPE_CON, ...)
* @param[in] token token
* @param[in] token_len length of @p token
* @param[in] code CoAP code (e.g., COAP_CODE_204, ...)
* @param[in] id CoAP request id
*
* @returns length of resulting header
*/
ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token,
size_t token_len, unsigned code, uint16_t id);
/**
* @brief Insert a CoAP option into buffer
*
* This function writes a CoAP option with nr. @p onum to @p buf.
* It handles calculating the option delta (from @p lastonum), encoding the
* length from @p olen and copying the option data from @p odata.
*
* @param[out] buf buffer to write to
* @param[in] lastonum number of previous option (for delta calculation),
* or 0 for first option
* @param[in] onum number of option
* @param[in] odata ptr to raw option data (or NULL)
* @param[in] olen length of @p odata (if any)
*
* @returns amount of bytes written to @p buf
*/
size_t coap_put_option(uint8_t *buf, uint16_t lastonum, uint16_t onum, uint8_t *odata, size_t olen);
/**
* @brief Insert content type option into buffer
*
* @param[out] buf buffer to write to
* @param[in] lastonum number of previous option (for delta
* calculation), or 0 if first option
* @param[in] content_type content type to set
*
* @returns amount of bytes written to @p buf
*/
size_t coap_put_option_ct(uint8_t *buf, uint16_t lastonum, uint16_t content_type);
/**
* @brief Insert URI encoded option into buffer
*
* @param[out] buf buffer to write to
* @param[in] lastonum number of previous option (for delta calculation),
* or 0 if first option
* @param[in] uri ptr to source URI
* @param[in] optnum option number to use (e.g., COAP_OPT_URI_PATH)
*
* @returns amount of bytes written to @p buf
*/
size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uint16_t optnum);
/**
* @brief Get the CoAP version number
*
* @param[in] pkt CoAP packet
*
* @returns CoAP version number
*/
static inline unsigned coap_get_ver(coap_pkt_t *pkt)
{
return (pkt->hdr->ver_t_tkl & 0x60) >> 6;
}
/**
* @brief Get the message type
*
* @param[in] pkt CoAP packet
*
* @returns COAP_TYPE_CON
* @returns COAP_TYPE_NON
* @returns COAP_TYPE_ACK
* @returns COAP_TYPE_RST
*/
static inline unsigned coap_get_type(coap_pkt_t *pkt)
{
return (pkt->hdr->ver_t_tkl & 0x30) >> 4;
}
/**
* @brief Get a message's token length [in byte]
*
* @param[in] pkt CoAP packet
*
* @returns length of token in the given message (0-8 byte)
*/
static inline unsigned coap_get_token_len(coap_pkt_t *pkt)
{
return (pkt->hdr->ver_t_tkl & 0xf);
}
/**
* @brief Get a message's code class (3 most significant bits of code)
*
* @param[in] pkt CoAP packet
*
* @returns message code class
*/
static inline unsigned coap_get_code_class(coap_pkt_t *pkt)
{
return pkt->hdr->code >> 5;
}
/**
* @brief Get a message's code detail (5 least significant bits of code)
*
* @param[in] pkt CoAP packet
*
* @returns message code detail
*/
static inline unsigned coap_get_code_detail(coap_pkt_t *pkt)
{
return pkt->hdr->code & 0x1f;
}
/**
* @brief Get a message's raw code (class + detail)
*
* @param[in] pkt CoAP packet
*
* @returns raw message code
*/
static inline unsigned coap_get_code_raw(coap_pkt_t *pkt)
{
return (unsigned)pkt->hdr->code;
}
/**
* @brief Get a message's code in decimal format ((class * 100) + detail)
*
* @param[in] pkt CoAP packet
*
* @returns message code in decimal format
*/
static inline unsigned coap_get_code(coap_pkt_t *pkt)
{
return (coap_get_code_class(pkt) * 100) + coap_get_code_detail(pkt);
}
/**
* @brief Get the message ID of the given CoAP packet
*
* @param[in] pkt CoAP packet
*
* @returns message ID
*/
static inline unsigned coap_get_id(coap_pkt_t *pkt)
{
return ntohs(pkt->hdr->id);
}
/**
* @brief Get the total header length (4-byte header + token length)
*
* @param[in] pkt CoAP packet
*
* @returns total header length
*/
static inline unsigned coap_get_total_hdr_len(coap_pkt_t *pkt)
{
return sizeof(coap_hdr_t) + coap_get_token_len(pkt);
}
/**
* @brief Encode given code class and code detail to raw code
*
* @param[in] class message code class
* @param[in] detail message code detail
*
* @returns raw message code
*/
static inline uint8_t coap_code(unsigned class, unsigned detail)
{
return (class << 5) | detail;
}
/**
* @brief Write the given raw message code to given CoAP header
*
* @param[out] hdr CoAP header to write to
* @param[in] code raw message code
*/
static inline void coap_hdr_set_code(coap_hdr_t *hdr, uint8_t code)
{
hdr->code = code;
}
/**
* @brief Set the message type for the given CoAP header
*
* @pre (type := [0-3])
*
* @param[out] hdr CoAP header to write
* @param[in] type message type as integer value [0-3]
*/
static inline void coap_hdr_set_type(coap_hdr_t *hdr, unsigned type)
{
/* assert correct range of type */
assert(!(type & ~0x3));
hdr->ver_t_tkl &= ~0x30;
hdr->ver_t_tkl |= type << 4;
}
/**
* @brief Convert message code (request method) into a corresponding bit field
*
* @param[in] code request code denoting the request method
*
* @returns bit field corresponding to the given request method
*/
static inline unsigned coap_method2flag(unsigned code)
{
return (1 << (code - 1));
}
/**
* @brief Identifies a packet containing an observe option
*
* @param[in] pkt CoAP packet
*
* @returns true if observe value is set
* @returns false if not
*/
static inline bool coap_has_observe(coap_pkt_t *pkt)
{
return pkt->observe_value != UINT32_MAX;
}
/**
* @brief Clears the observe option value from a packet
*
* @param[in] pkt CoAP packet
*/
static inline void coap_clear_observe(coap_pkt_t *pkt)
{
pkt->observe_value = UINT32_MAX;
}
/**
* @brief Get the value of the observe option from the given packet
*
* @param[in] pkt CoAP packet
*
* @returns value of the observe option
*/
static inline uint32_t coap_get_observe(coap_pkt_t *pkt)
{
return pkt->observe_value;
}
/**
* @brief Reference to the default .well-known/core handler defined by the
* application
*/
extern ssize_t coap_well_known_core_default_handler(coap_pkt_t *pkt, \
uint8_t *buf, size_t len);
/**
* @brief Resource definition for the default .well-known/core handler
*/
#define COAP_WELL_KNOWN_CORE_DEFAULT_HANDLER \
{ "/.well-known/core", COAP_GET, coap_well_known_core_default_handler }
#ifdef __cplusplus
}
#endif
#endif /* NET_NANOCOAP_H */
/** @} */

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2017 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 sys_net_nanocoap
*
* @{
*
* @file
* @brief nanocoap high-level API
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*/
#ifndef NET_NANOCOAP_SOCK_H
#define NET_NANOCOAP_SOCK_H
#include <stdint.h>
#include <unistd.h>
#include "net/sock/udp.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Start a nanocoap server instance
*
* This function only returns if there's an error binding to @p local, or if
* receiving of UDP packets fails.
*
* @param[in] local local UDP endpoint to bind to
* @param[in] buf input buffer to use
* @param[in] bufsize size of @p buf
*
* @returns -1 on error
*/
int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize);
/**
* @brief Simple synchronous CoAP get
*
* @param[in] remote remote UDP endpoint
* @param[in] path remote path
* @param[out] buf buffer to write response to
* @param[in] len length of @p buffer
*
* @returns length of response on success
* @returns <0 on error
*/
ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf,
size_t len);
#ifdef __cplusplus
}
#endif
#endif /* NET_NANOCOAP_SOCK_H */
/** @} */

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,396 @@
/*
* Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 sys_net_nanocoap
* @{
*
* @file
* @brief Nanocoap implementation
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "net/nanocoap.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end);
static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes);
/* http://tools.ietf.org/html/rfc7252#section-3
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |Ver| T | TKL | Code | Message ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Token (if any, TKL bytes) ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Options (if any) ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |1 1 1 1 1 1 1 1| Payload (if any) ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
{
uint8_t *urlpos = pkt->url;
coap_hdr_t *hdr = (coap_hdr_t *)buf;
pkt->hdr = hdr;
uint8_t *pkt_pos = hdr->data;
uint8_t *pkt_end = buf + len;
memset(pkt->url, '\0', NANOCOAP_URL_MAX);
pkt->payload_len = 0;
pkt->observe_value = UINT32_MAX;
/* token value (tkl bytes) */
if (coap_get_token_len(pkt)) {
pkt->token = pkt_pos;
pkt_pos += coap_get_token_len(pkt);
} else {
pkt->token = NULL;
}
/* parse options */
int option_nr = 0;
while (pkt_pos != pkt_end) {
uint8_t option_byte = *pkt_pos++;
if (option_byte == 0xff) {
pkt->payload = pkt_pos;
pkt->payload_len = buf + len - pkt_pos;
DEBUG("payload len = %u\n", pkt->payload_len);
break;
}
else {
int option_delta = _decode_value(option_byte >> 4, &pkt_pos, pkt_end);
if (option_delta < 0) {
DEBUG("bad op delta\n");
return -EBADMSG;
}
int option_len = _decode_value(option_byte & 0xf, &pkt_pos, pkt_end);
if (option_len < 0) {
DEBUG("bad op len\n");
return -EBADMSG;
}
option_nr += option_delta;
DEBUG("option nr=%i len=%i\n", option_nr, option_len);
switch (option_nr) {
case COAP_OPT_URI_PATH:
*urlpos++ = '/';
memcpy(urlpos, pkt_pos, option_len);
urlpos += option_len;
break;
case COAP_OPT_CONTENT_FORMAT:
if (option_len == 0) {
pkt->content_type = 0;
} else if (option_len == 1) {
pkt->content_type = *pkt_pos;
} else if (option_len == 2) {
memcpy(&pkt->content_type, pkt_pos, 2);
pkt->content_type = ntohs(pkt->content_type);
}
break;
case COAP_OPT_OBSERVE:
if (option_len < 4) {
pkt->observe_value = _decode_uint(pkt_pos, option_len);
} else {
DEBUG("nanocoap: discarding packet with invalid option length.\n");
return -EBADMSG;
}
break;
default:
DEBUG("nanocoap: unhandled option nr=%i len=%i critical=%u\n", option_nr, option_len, option_nr & 1);
if (option_nr & 1) {
DEBUG("nanocoap: discarding packet with unknown critical option.\n");
return -EBADMSG;
}
}
pkt_pos += option_len;
}
}
DEBUG("coap pkt parsed. code=%u detail=%u payload_len=%u, 0x%02x\n",
coap_get_code_class(pkt),
coap_get_code_detail(pkt),
pkt->payload_len, hdr->code);
return 0;
}
ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_len)
{
if (coap_get_code_class(pkt) != COAP_REQ) {
DEBUG("coap_handle_req(): not a request.\n");
return -EBADMSG;
}
if (pkt->hdr->code == 0) {
return coap_build_reply(pkt, COAP_CODE_EMPTY, resp_buf, resp_buf_len, 0);
}
unsigned method_flag = coap_method2flag(coap_get_code_detail(pkt));
for (unsigned i = 0; i < coap_resources_numof; i++) {
if (! (coap_resources[i].methods & method_flag)) {
continue;
}
int res = strcmp((char*)pkt->url, coap_resources[i].path);
if (res > 0) {
continue;
}
else if (res < 0) {
break;
}
else {
return coap_resources[i].handler(pkt, resp_buf, resp_buf_len);
}
}
return coap_build_reply(pkt, COAP_CODE_404, resp_buf, resp_buf_len, 0);
}
ssize_t coap_reply_simple(coap_pkt_t *pkt,
unsigned code,
uint8_t *buf, size_t len,
unsigned ct,
const uint8_t *payload, uint8_t payload_len)
{
uint8_t *payload_start = buf + coap_get_total_hdr_len(pkt);
uint8_t *bufpos = payload_start;
if (payload_len) {
bufpos += coap_put_option_ct(bufpos, 0, ct);
*bufpos++ = 0xff;
memcpy(bufpos, payload, payload_len);
bufpos += payload_len;
}
return coap_build_reply(pkt, code, buf, len, bufpos - payload_start);
}
ssize_t coap_build_reply(coap_pkt_t *pkt, unsigned code,
uint8_t *rbuf, unsigned rlen, unsigned payload_len)
{
unsigned tkl = coap_get_token_len(pkt);
unsigned len = sizeof(coap_hdr_t) + tkl;
if ((len + payload_len + 1) > rlen) {
return -ENOSPC;
}
/* if code is COAP_CODE_EMPTY (zero), use RST as type, else RESP */
unsigned type = code ? COAP_RESP : COAP_RST;
coap_build_hdr((coap_hdr_t*)rbuf, type, pkt->token, tkl, code, pkt->hdr->id);
coap_hdr_set_type((coap_hdr_t*)rbuf, type);
coap_hdr_set_code((coap_hdr_t*)rbuf, code);
len += payload_len;
return len;
}
ssize_t coap_build_hdr(coap_hdr_t *hdr, unsigned type, uint8_t *token, size_t token_len, unsigned code, uint16_t id)
{
assert(!(type & ~0x3));
assert(!(token_len & ~0x1f));
memset(hdr, 0, sizeof(coap_hdr_t));
hdr->ver_t_tkl = (0x1 << 6) | (type << 4) | token_len;
hdr->code = code;
hdr->id = id;
if (token_len) {
memcpy(hdr->data, token, token_len);
}
return sizeof(coap_hdr_t) + token_len;
}
static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end)
{
uint8_t *pkt_pos = *pkt_pos_ptr;
size_t left = pkt_end - pkt_pos;
int res;
switch (val) {
case 13:
{
/* An 8-bit unsigned integer follows the initial byte and
indicates the Option Delta minus 13. */
if (left < 1) {
return -ENOSPC;
}
uint8_t delta = *pkt_pos++;
res = delta + 13;
break;
}
case 14:
{
/* A 16-bit unsigned integer in network byte order follows
* the initial byte and indicates the Option Delta minus
* 269. */
if (left < 2) {
return -ENOSPC;
}
uint16_t delta;
uint8_t *_tmp = (uint8_t*)&delta;
*_tmp++= *pkt_pos++;
*_tmp++= *pkt_pos++;
res = ntohs(delta) + 269;
break;
}
case 15:
/* Reserved for the Payload Marker. If the field is set to
* this value but the entire byte is not the payload
* marker, this MUST be processed as a message format
* error. */
return -EBADMSG;
default:
res = val;
}
*pkt_pos_ptr = pkt_pos;
return res;
}
static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes)
{
assert(nbytes <= 4);
uint32_t res = 0;
if (nbytes) {
memcpy(((uint8_t*)&res) + (4 - nbytes), pkt_pos, nbytes);
}
return ntohl(res);
}
static unsigned _put_delta_optlen(uint8_t *buf, unsigned offset, unsigned shift, unsigned val)
{
if (val < 13) {
*buf |= (val << shift);
}
else if (val < (256 + 13)) {
*buf |= (13 << shift);
buf[offset++] = (val - 13);
}
else {
*buf |= (14 << shift);
uint16_t tmp = (val - 269);
tmp = htons(tmp);
memcpy(buf + offset, &tmp, 2);
offset += 2;
}
return offset;
}
size_t coap_put_option(uint8_t *buf, uint16_t lastonum, uint16_t onum, uint8_t *odata, size_t olen)
{
assert(lastonum <= onum);
unsigned delta = (onum - lastonum);
*buf = 0;
/* write delta value to option header: 4 upper bits of header (shift 4) +
* 1 or 2 optional bytes depending on delta value) */
unsigned n = _put_delta_optlen(buf, 1, 4, delta);
/* write option length to option header: 4 lower bits of header (shift 0) +
* 1 or 2 optional bytes depending of the length of the option */
n = _put_delta_optlen(buf, n, 0, olen);
if(olen) {
memcpy(buf + n, odata, olen);
n += olen;
}
return (size_t)n;
}
size_t coap_put_option_ct(uint8_t *buf, uint16_t lastonum, uint16_t content_type)
{
if (content_type == 0) {
return coap_put_option(buf, lastonum, COAP_OPT_CONTENT_FORMAT, NULL, 0);
}
else if (content_type <= 255) {
uint8_t tmp = content_type;
return coap_put_option(buf, lastonum, COAP_OPT_CONTENT_FORMAT, &tmp, sizeof(tmp));
}
else {
return coap_put_option(buf, lastonum, COAP_OPT_CONTENT_FORMAT, (uint8_t*)&content_type, sizeof(content_type));
}
}
size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uint16_t optnum)
{
char separator = (optnum == COAP_OPT_URI_PATH) ? '/' : '&';
size_t uri_len = strlen(uri);
if (uri_len == 0) {
return 0;
}
uint8_t *bufpos = buf;
char *uripos = (char*)uri;
while(uri_len) {
size_t part_len;
uripos++;
uint8_t *part_start = (uint8_t*)uripos;
while (uri_len--) {
if ((*uripos == separator) || (*uripos == '\0')) {
break;
}
uripos++;
}
part_len = (uint8_t*)uripos - part_start;
if (part_len) {
bufpos += coap_put_option(bufpos, lastonum, optnum, part_start, part_len);
lastonum = optnum;
}
}
return bufpos - buf;
}
ssize_t coap_well_known_core_default_handler(coap_pkt_t* pkt, uint8_t *buf, \
size_t len)
{
uint8_t *payload = buf + coap_get_total_hdr_len(pkt);
uint8_t *bufpos = payload;
bufpos += coap_put_option_ct(bufpos, 0, COAP_CT_LINK_FORMAT);
*bufpos++ = 0xff;
for (unsigned i = 0; i < coap_resources_numof; i++) {
if (i) {
*bufpos++ = ',';
}
*bufpos++ = '<';
unsigned url_len = strlen(coap_resources[i].path);
memcpy(bufpos, coap_resources[i].path, url_len);
bufpos += url_len;
*bufpos++ = '>';
}
unsigned payload_len = bufpos - payload;
return coap_build_reply(pkt, COAP_CODE_205, buf, len, payload_len);
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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 sys_net_nanocoap
* @{
*
* @file
* @brief Nanocoap sock helpers
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include "net/nanocoap.h"
#include "net/sock/udp.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
ssize_t nanocoap_get(sock_udp_ep_t *remote, const char *path, uint8_t *buf, size_t len)
{
ssize_t res;
sock_udp_t sock;
if (!remote->port) {
remote->port = COAP_PORT;
}
res = sock_udp_create(&sock, NULL, remote, 0);
if (res < 0) {
return res;
}
uint8_t *pktpos = buf;
pktpos += coap_build_hdr((coap_hdr_t *)pktpos, COAP_REQ, NULL, 0, COAP_METHOD_GET, 1);
pktpos += coap_put_option_uri(pktpos, 0, path, COAP_OPT_URI_PATH);
/* TODO: timeout random between between ACK_TIMEOUT and (ACK_TIMEOUT *
* ACK_RANDOM_FACTOR) */
uint32_t timeout = COAP_ACK_TIMEOUT * (1000000U);
int tries = 0;
while (tries++ < COAP_MAX_RETRANSMIT) {
if (!tries) {
DEBUG("nanocoap: maximum retries reached.\n");
res = -ETIMEDOUT;
goto out;
}
res = sock_udp_send(&sock, buf, pktpos - buf, NULL);
if (res <= 0) {
DEBUG("nanocoap: error sending coap request\n");
goto out;
}
res = sock_udp_recv(&sock, buf, len, timeout, NULL);
if (res <= 0) {
if (res == -ETIMEDOUT) {
DEBUG("nanocoap: timeout\n");
timeout *= 2;
continue;
}
DEBUG("nanocoap: error receiving coap request\n");
break;
}
coap_pkt_t pkt;
if (coap_parse(&pkt, (uint8_t *)buf, res) < 0) {
puts("error parsing packet");
continue;
}
else {
res = coap_get_code(&pkt);
if (res != 205) {
res = -res;
}
else {
if (pkt.payload_len) {
memcpy(buf, pkt.payload, pkt.payload_len);
}
res = pkt.payload_len;
}
break;
}
}
out:
sock_udp_close(&sock);
return res;
}
int nanocoap_server(sock_udp_ep_t *local, uint8_t *buf, size_t bufsize)
{
sock_udp_t sock;
sock_udp_ep_t remote;
if (!local->port) {
local->port = COAP_PORT;
}
ssize_t res = sock_udp_create(&sock, local, NULL, 0);
if (res == -1) {
return -1;
}
while (1) {
res = sock_udp_recv(&sock, buf, bufsize, -1, &remote);
if (res == -1) {
DEBUG("error receiving UDP packet\n");
return -1;
}
else {
coap_pkt_t pkt;
if (coap_parse(&pkt, (uint8_t *)buf, res) < 0) {
DEBUG("error parsing packet\n");
continue;
}
if ((res = coap_handle_req(&pkt, buf, bufsize)) > 0) {
res = sock_udp_send(&sock, buf, res, &remote);
}
}
}
return 0;
}