mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-12-17 02:23:49 +01:00
471 lines
13 KiB
C
471 lines
13 KiB
C
/*
|
|
* Copyright (C) 2024-2025 Carl Seifert
|
|
* Copyright (C) 2024-2025 TU Dresden
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* @ingroup net_unicoap
|
|
* @brief Buffer and string tools
|
|
* @author Carl Seifert <carl.seifert1@mailbox.tu-dresden.de>
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "iolist.h"
|
|
|
|
#include "net/unicoap/options.h"
|
|
#include "net/unicoap/message.h"
|
|
|
|
#define ENABLE_DEBUG CONFIG_UNICOAP_DEBUG_LOGGING
|
|
#include "debug.h"
|
|
#include "private.h"
|
|
|
|
static inline void iolist_init(iolist_t* iolist, uint8_t* buffer, size_t size, iolist_t* next)
|
|
{
|
|
iolist->iol_base = buffer;
|
|
iolist->iol_len = size;
|
|
iolist->iol_next = next;
|
|
}
|
|
|
|
static inline bool iolist_is_empty(const iolist_t* iolist)
|
|
{
|
|
while (iolist) {
|
|
if (iolist->iol_len > 0) {
|
|
return false;
|
|
}
|
|
iolist = iolist->iol_next;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void iolist_append(iolist_t** iolist, iolist_t* chunk)
|
|
{
|
|
assert(iolist);
|
|
if (*iolist) {
|
|
iolist_t* v = *iolist;
|
|
while (v->iol_next) {
|
|
v = v->iol_next;
|
|
}
|
|
v->iol_next = chunk;
|
|
}
|
|
else {
|
|
*iolist = chunk;
|
|
}
|
|
}
|
|
|
|
bool unicoap_message_payload_is_empty(const unicoap_message_t* message)
|
|
{
|
|
switch (message->payload_representation) {
|
|
case UNICOAP_PAYLOAD_NONCONTIGUOUS:
|
|
return iolist_is_empty(message->payload_chunks);
|
|
case UNICOAP_PAYLOAD_CONTIGUOUS:
|
|
return message->payload_size == 0;
|
|
default:
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ssize_t unicoap_message_payload_copy(const unicoap_message_t* message, uint8_t* buffer,
|
|
size_t capacity)
|
|
{
|
|
switch (message->payload_representation) {
|
|
case UNICOAP_PAYLOAD_NONCONTIGUOUS:
|
|
return iolist_to_buffer(message->payload_chunks, buffer, capacity);
|
|
case UNICOAP_PAYLOAD_CONTIGUOUS:
|
|
if (message->payload_size > capacity) {
|
|
UNICOAP_DEBUG("buf too small " _UNICOAP_NEED_HAVE "\n",
|
|
message->payload_size, capacity);
|
|
return -ENOBUFS;
|
|
}
|
|
memcpy(buffer, message->payload, message->payload_size);
|
|
return message->payload_size;
|
|
default:
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
ssize_t unicoap_message_payload_make_contiguous(unicoap_message_t* message, uint8_t* buffer,
|
|
size_t capacity)
|
|
{
|
|
switch (message->payload_representation) {
|
|
case UNICOAP_PAYLOAD_NONCONTIGUOUS: {
|
|
assert(buffer);
|
|
ssize_t res = iolist_to_buffer(message->payload_chunks, buffer, capacity);
|
|
if (res < 0) {
|
|
UNICOAP_DEBUG("buf too small " _UNICOAP_NEED_HAVE "\n",
|
|
iolist_size(message->payload_chunks), capacity);
|
|
return res;
|
|
}
|
|
message->payload_representation = UNICOAP_PAYLOAD_CONTIGUOUS;
|
|
message->payload = buffer;
|
|
message->payload_size = res;
|
|
return res;
|
|
}
|
|
case UNICOAP_PAYLOAD_CONTIGUOUS:
|
|
return message->payload_size;
|
|
default:
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void unicoap_message_payload_append_chunk(unicoap_message_t* message, iolist_t* chunk)
|
|
{
|
|
assert(message->payload_representation == UNICOAP_PAYLOAD_NONCONTIGUOUS);
|
|
if (message->payload_chunks) {
|
|
iolist_append(&message->payload_chunks, chunk);
|
|
}
|
|
else {
|
|
message->payload_chunks = chunk;
|
|
}
|
|
}
|
|
|
|
static inline iolist_t* _append_payload_to_iolist(const unicoap_message_t* message,
|
|
iolist_t* element)
|
|
{
|
|
switch (message->payload_representation) {
|
|
case UNICOAP_PAYLOAD_NONCONTIGUOUS:
|
|
return message->payload_chunks;
|
|
case UNICOAP_PAYLOAD_CONTIGUOUS:
|
|
iolist_init(element, message->payload, message->payload_size, NULL);
|
|
return element;
|
|
default:
|
|
UNREACHABLE();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static const uint8_t _payload_separator = 0xff;
|
|
|
|
int unicoap_pdu_buildv_options_and_payload(uint8_t* header, size_t header_size,
|
|
const unicoap_message_t* message,
|
|
iolist_t iolists[UNICOAP_PDU_IOLIST_COUNT])
|
|
{
|
|
assert(header);
|
|
assert(message);
|
|
assert(iolists);
|
|
iolist_init(iolists, header, header_size, NULL);
|
|
|
|
iolist_t* element = iolists;
|
|
if (message->options && message->options->option_count > 0) {
|
|
element->iol_next = element + 1;
|
|
element += 1;
|
|
iolist_init(element, unicoap_message_options_data(message),
|
|
unicoap_message_options_size(message), NULL);
|
|
}
|
|
|
|
if (unicoap_message_payload_get_size(message) > 0) {
|
|
element->iol_next = element + 1;
|
|
element += 1;
|
|
|
|
iolist_init(element, (uint8_t*)&_payload_separator, 1, element + 1);
|
|
|
|
element->iol_next = _append_payload_to_iolist(message, element + 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
ssize_t unicoap_pdu_build_options_and_payload(uint8_t* pdu, size_t capacity,
|
|
const unicoap_message_t* message)
|
|
{
|
|
assert(pdu);
|
|
assert(message);
|
|
|
|
if (message->options && message->options->option_count > 0) {
|
|
if (capacity < unicoap_message_options_size(message)) {
|
|
return -ENOBUFS;
|
|
}
|
|
memcpy(pdu, unicoap_message_options_data(message), unicoap_message_options_size(message));
|
|
pdu += unicoap_message_options_size(message);
|
|
capacity -= unicoap_message_options_size(message);
|
|
}
|
|
|
|
if (unicoap_message_payload_get_size(message) > 0) {
|
|
*pdu = UNICOAP_PAYLOAD_MARKER;
|
|
pdu += 1;
|
|
capacity -= 1;
|
|
|
|
ssize_t payload_size = 0;
|
|
if ((payload_size = unicoap_message_payload_copy(message, pdu, capacity)) < 0) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
return unicoap_message_options_size(message) + 1 + payload_size;
|
|
}
|
|
else {
|
|
return unicoap_message_options_size(message);
|
|
}
|
|
}
|
|
|
|
bool unicoap_message_is_response(uint8_t code)
|
|
{
|
|
switch (unicoap_code_class(code)) {
|
|
case UNICOAP_CODE_CLASS_RESPONSE_SUCCESS:
|
|
case UNICOAP_CODE_CLASS_RESPONSE_CLIENT_FAILURE:
|
|
case UNICOAP_CODE_CLASS_RESPONSE_SERVER_FAILURE:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const char* unicoap_string_from_code_class(uint8_t code)
|
|
{
|
|
if (code == UNICOAP_CODE_EMPTY) {
|
|
return "EMPTY";
|
|
}
|
|
|
|
switch (unicoap_code_class(code)) {
|
|
case UNICOAP_CODE_CLASS_REQUEST:
|
|
return "REQ";
|
|
|
|
case UNICOAP_CODE_CLASS_RESPONSE_SUCCESS:
|
|
case UNICOAP_CODE_CLASS_RESPONSE_CLIENT_FAILURE:
|
|
case UNICOAP_CODE_CLASS_RESPONSE_SERVER_FAILURE:
|
|
return "RESP";
|
|
|
|
case UNICOAP_CODE_CLASS_SIGNAL:
|
|
return "SIGNAL";
|
|
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
const char* unicoap_string_from_code(uint8_t code)
|
|
{
|
|
if (code == UNICOAP_CODE_EMPTY) {
|
|
return "Empty";
|
|
}
|
|
|
|
switch (unicoap_code_class(code)) {
|
|
case UNICOAP_CODE_CLASS_REQUEST:
|
|
return unicoap_string_from_method((unicoap_method_t)code);
|
|
|
|
case UNICOAP_CODE_CLASS_RESPONSE_SUCCESS:
|
|
case UNICOAP_CODE_CLASS_RESPONSE_CLIENT_FAILURE:
|
|
case UNICOAP_CODE_CLASS_RESPONSE_SERVER_FAILURE:
|
|
return unicoap_string_from_status(code);
|
|
|
|
case UNICOAP_CODE_CLASS_SIGNAL:
|
|
return unicoap_string_from_signal(code);
|
|
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
const char* unicoap_string_from_signal(unicoap_signal_t signal)
|
|
{
|
|
switch (signal) {
|
|
case UNICOAP_SIGNAL_CAPABILITIES_SETTINGS:
|
|
return "CSM";
|
|
case UNICOAP_SIGNAL_PING:
|
|
return "Ping";
|
|
case UNICOAP_SIGNAL_PONG:
|
|
return "Pong";
|
|
case UNICOAP_SIGNAL_ABORT:
|
|
return "Abort";
|
|
case UNICOAP_SIGNAL_RELEASE:
|
|
return "Release";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
const char* unicoap_string_from_method(unicoap_method_t method)
|
|
{
|
|
switch (method) {
|
|
case UNICOAP_METHOD_GET:
|
|
return "GET";
|
|
case UNICOAP_METHOD_PUT:
|
|
return "PUT";
|
|
case UNICOAP_METHOD_POST:
|
|
return "POST";
|
|
case UNICOAP_METHOD_PATCH:
|
|
return "PATCH";
|
|
case UNICOAP_METHOD_IPATCH:
|
|
return "iPATCH";
|
|
case UNICOAP_METHOD_FETCH:
|
|
return "FETCH";
|
|
case UNICOAP_METHOD_DELETE:
|
|
return "DELETE";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
const char* unicoap_string_from_status(unicoap_status_t status)
|
|
{
|
|
switch (status) {
|
|
case UNICOAP_STATUS_CREATED:
|
|
return "Created";
|
|
case UNICOAP_STATUS_DELETED:
|
|
return "Deleted";
|
|
case UNICOAP_STATUS_VALID:
|
|
return "Valid";
|
|
case UNICOAP_STATUS_CHANGED:
|
|
return "Changed";
|
|
case UNICOAP_STATUS_CONTENT:
|
|
return "Content";
|
|
case UNICOAP_STATUS_CONTINUE:
|
|
return "Continue";
|
|
case UNICOAP_STATUS_BAD_REQUEST:
|
|
return "Bad Request";
|
|
case UNICOAP_STATUS_UNAUTHORIZED:
|
|
return "Unauthorized";
|
|
case UNICOAP_STATUS_BAD_OPTION:
|
|
return "Bad Option";
|
|
case UNICOAP_STATUS_FORBIDDEN:
|
|
return "Forbidden";
|
|
case UNICOAP_STATUS_PATH_NOT_FOUND:
|
|
return "Not Found";
|
|
case UNICOAP_STATUS_METHOD_NOT_ALLOWED:
|
|
return "Method Not Allowed";
|
|
case UNICOAP_STATUS_NOT_ACCEPTABLE:
|
|
return "Not Acceptable";
|
|
case UNICOAP_STATUS_REQUEST_ENTITY_INCOMPLETE:
|
|
return "Request Entity Incomplete";
|
|
case UNICOAP_STATUS_CONFLICT:
|
|
return "Conflict";
|
|
case UNICOAP_STATUS_PRECONDITION_FAILED:
|
|
return "Precondition Failed";
|
|
case UNICOAP_STATUS_REQUEST_ENTITY_TOO_LARGE:
|
|
return "Request Entity Too Large";
|
|
case UNICOAP_STATUS_UNSUPPORTED_CONTENT_FORMAT:
|
|
return "Unsupported Content Format";
|
|
case UNICOAP_STATUS_UNPROCESSABLE_ENTITY:
|
|
return "Unprocessable Entity";
|
|
case UNICOAP_STATUS_TOO_MANY_REQUESTS:
|
|
return "Too Many Requests";
|
|
case UNICOAP_STATUS_INTERNAL_SERVER_ERROR:
|
|
return "Internal Server Error";
|
|
case UNICOAP_STATUS_NOT_IMPLEMENTED:
|
|
return "Not Implemented";
|
|
case UNICOAP_STATUS_BAD_GATEWAY:
|
|
return "Bad Gateway";
|
|
case UNICOAP_STATUS_SERVICE_UNAVAILABLE:
|
|
return "Service Unavailable";
|
|
case UNICOAP_STATUS_GATEWAY_TIMEOUT:
|
|
return "Gateway Timeout";
|
|
case UNICOAP_STATUS_PROXYING_NOT_SUPPORTED:
|
|
return "Proxying Not Supported";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
const char* unicoap_string_from_option_number(unicoap_option_number_t number)
|
|
{
|
|
switch (number) {
|
|
case UNICOAP_OPTION_SIZE1:
|
|
return "Size1";
|
|
case UNICOAP_OPTION_SIZE2:
|
|
return "Size2";
|
|
case UNICOAP_OPTION_BLOCK1:
|
|
return "Block1";
|
|
case UNICOAP_OPTION_BLOCK2:
|
|
return "Block2";
|
|
case UNICOAP_OPTION_Q_BLOCK1:
|
|
return "Q-Block1";
|
|
case UNICOAP_OPTION_Q_BLOCK2:
|
|
return "Q-Block2";
|
|
case UNICOAP_OPTION_ECHO:
|
|
return "Echo";
|
|
case UNICOAP_OPTION_ETAG:
|
|
return "ETag";
|
|
case UNICOAP_OPTION_EDHOC:
|
|
return "EDHOC";
|
|
case UNICOAP_OPTION_ACCEPT:
|
|
return "Accept";
|
|
case UNICOAP_OPTION_OSCORE:
|
|
return "OSCORE";
|
|
case UNICOAP_OPTION_OBSERVE:
|
|
return "Observe";
|
|
case UNICOAP_OPTION_MAX_AGE:
|
|
return "Max-Age";
|
|
case UNICOAP_OPTION_IF_MATCH:
|
|
return "If-Match";
|
|
case UNICOAP_OPTION_IF_NONE_MATCH:
|
|
return "If-None-Match";
|
|
case UNICOAP_OPTION_LOCATION_PATH:
|
|
return "Location-Path";
|
|
case UNICOAP_OPTION_LOCATION_QUERY:
|
|
return "Location-Query";
|
|
case UNICOAP_OPTION_URI_HOST:
|
|
return "Uri-Host";
|
|
case UNICOAP_OPTION_URI_PORT:
|
|
return "Uri-Port";
|
|
case UNICOAP_OPTION_URI_QUERY:
|
|
return "Uri-Query";
|
|
case UNICOAP_OPTION_URI_PATH:
|
|
return "Uri-Path";
|
|
case UNICOAP_OPTION_HOP_LIMIT:
|
|
return "Hop-Limit";
|
|
case UNICOAP_OPTION_PROXY_URI:
|
|
return "Proxy-Uri";
|
|
case UNICOAP_OPTION_PROXY_SCHEME:
|
|
return "Proxy-Scheme";
|
|
case UNICOAP_OPTION_NO_RESPONSE:
|
|
return "No-Response";
|
|
case UNICOAP_OPTION_REQUEST_TAG:
|
|
return "Request-Tag";
|
|
case UNICOAP_OPTION_CONTENT_FORMAT:
|
|
return "Content-Format";
|
|
default:
|
|
return "?";
|
|
}
|
|
}
|
|
|
|
int unicoap_print_code(uint8_t code, char* string)
|
|
{
|
|
return sprintf(string, UNICOAP_CODE_CLASS_DETAIL_FORMAT, unicoap_code_class(code),
|
|
unicoap_code_detail(code));
|
|
}
|
|
|
|
bool unicoap_response_is_optional(unicoap_options_t* options, unicoap_status_t status)
|
|
{
|
|
uint8_t no_response;
|
|
if (unicoap_options_get_no_response(options, &no_response) >= 0) {
|
|
const uint8_t no_response_index = (status >> 5) - 1;
|
|
/* if the handler code misbehaved here, we'd face UB otherwise */
|
|
assert(no_response_index < 7);
|
|
const uint8_t mask = 1 << no_response_index;
|
|
|
|
/* option contains bitmap of disinterest */
|
|
if (no_response & mask) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void unicoap_options_dump_all(const unicoap_options_t* options)
|
|
{
|
|
unicoap_options_iterator_t iterator = { 0 };
|
|
unicoap_options_iterator_init(&iterator, (unicoap_options_t*)options);
|
|
const uint8_t* value = NULL;
|
|
ssize_t size = -1;
|
|
unicoap_option_number_t number = 0;
|
|
|
|
while ((size = unicoap_options_get_next(&iterator, &number, &value)) >= 0) {
|
|
printf("<%s nr=%u size=%" PRIuSIZE " hex=", unicoap_string_from_option_number(number),
|
|
number, size);
|
|
for (int i = 0; i < size; i += 1) {
|
|
printf("%02X", value[i]);
|
|
}
|
|
printf(">\n");
|
|
}
|
|
}
|