From 7b7c63b63873900db7a2072ed02e4a0f5a5c0026 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Sun, 24 Apr 2022 23:47:01 +0200 Subject: [PATCH 1/2] nanocoap_link_format: add helper function to parse link format --- sys/Makefile.dep | 4 + sys/include/net/nanocoap/link_format.h | 72 ++++++++++++ .../application_layer/nanocoap/link_format.c | 109 ++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 sys/include/net/nanocoap/link_format.h create mode 100644 sys/net/application_layer/nanocoap/link_format.c diff --git a/sys/Makefile.dep b/sys/Makefile.dep index b24eef8d08e33..5a4501397c71c 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -703,6 +703,10 @@ ifneq (,$(filter nanocoap_cache,$(USEMODULE))) USEMODULE += hashes endif +ifneq (,$(filter nanocoap_link_format,$(USEMODULE))) + USEMODULE += fmt +endif + ifneq (,$(filter nanocoap_vfs,$(USEMODULE))) USEMODULE += nanocoap_sock USEMODULE += vfs diff --git a/sys/include/net/nanocoap/link_format.h b/sys/include/net/nanocoap/link_format.h new file mode 100644 index 0000000000000..e0ea047d32629 --- /dev/null +++ b/sys/include/net/nanocoap/link_format.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 ML!PA Consulting GmbH + * + * 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_nanosock + * @brief NanoCoAP Link Format helper functions + * + * @{ + * + * @file + * @brief NanoCoAP Link Format ([RFC 6690](https://www.rfc-editor.org/rfc/rfc6690.html)) + * helper functions + * + * @author Benjamin Valentin + */ +#ifndef NET_NANOCOAP_LINK_FORMAT_H +#define NET_NANOCOAP_LINK_FORMAT_H + +#include "net/nanocoap_sock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Callback function called for each resource on the directory + * + * @param[in] entry Resource entry from the server + * @param[in] ctx Optional function context + * + * @returns 0 on success + * @returns <0 on error + */ +typedef int (*coap_link_format_handler_t)(char *entry, void *ctx); + +/** + * @brief Downloads the resource behind @p path via blockwise GET + * + * @param[in] sock Connection to the server + * @param[in] path path of the resource + * @param[in] cb Callback to execute for each resource entry + * @param[in] arg Optional callback argument + * + * @returns 0 on success + * @returns <0 on error + */ +int nanocoap_link_format_get(nanocoap_sock_t *sock, const char *path, + coap_link_format_handler_t cb, void *arg); + +/** + * @brief Downloads the resource behind @p url via blockwise GET + * + * @param[in] url URL to the resource + * @param[in] cb Callback to execute for each resource entry + * @param[in] arg Optional callback argument + * + * @returns 0 on success + * @returns <0 on error + */ +int nanocoap_link_format_get_url(const char *url, + coap_link_format_handler_t cb, void *arg); + +#ifdef __cplusplus +} +#endif +#endif /* NET_NANOCOAP_LINK_FORMAT_H */ +/** @} */ diff --git a/sys/net/application_layer/nanocoap/link_format.c b/sys/net/application_layer/nanocoap/link_format.c new file mode 100644 index 0000000000000..c80db58dd93ee --- /dev/null +++ b/sys/net/application_layer/nanocoap/link_format.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2022 ML!PA Consulting GmbH + * + * 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_nanocoap + * @{ + * + * @file + * @brief NanoCoAP Link Format parser + * + * @author Benjamin Valentin + * + * @} + */ + +#include "fmt.h" +#include "net/nanocoap/link_format.h" +#include "net/nanocoap_sock.h" +#include "net/sock/util.h" + +struct dir_list_ctx { + char *buf; + char *cur; + char *end; + coap_link_format_handler_t cb; + void *ctx; + char esc_buf[2]; + uint8_t esc_idx; +}; + +static int _dirlist_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more) +{ + (void)offset; + + struct dir_list_ctx *ctx = arg; + + char *end = (char *)buf + len; + for (char *c = (char *)buf; c != end; ++c) { + + /* start of escape sequence */ + if (*c == '%') { + ctx->esc_idx = 1; + continue; + } + if (ctx->esc_idx) { + /* fill escape buffer */ + ctx->esc_buf[ctx->esc_idx - 1] = *c; + if (++ctx->esc_idx == 3) { + ctx->esc_idx = 0; + *c = scn_u32_hex(ctx->esc_buf, 2); + } else { + continue; + } + } + + if (*c == ',' || ctx->cur == ctx->end) { + int res; + *ctx->cur = 0; + res = ctx->cb(ctx->buf, ctx->ctx); + ctx->cur = ctx->buf; + if (res < 0) { + return res; + } + } else { + *ctx->cur++ = *c; + } + } + + if (!more) { + *ctx->cur = 0; + return ctx->cb(ctx->buf, ctx->ctx); + } + + return 0; +} + +int nanocoap_link_format_get(nanocoap_sock_t *sock, const char *path, + coap_link_format_handler_t cb, void *arg) +{ + char buffer[CONFIG_NANOCOAP_QS_MAX]; + struct dir_list_ctx ctx = { + .buf = buffer, + .end = buffer + sizeof(buffer), + .cur = buffer, + .cb = cb, + .ctx = arg, + }; + return nanocoap_sock_get_blockwise(sock, path, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT, + _dirlist_cb, &ctx); +} + +int nanocoap_link_format_get_url(const char *url, coap_link_format_handler_t cb, void *arg) +{ + nanocoap_sock_t sock; + int res = nanocoap_sock_url_connect(url, &sock); + if (res) { + return res; + } + + res = nanocoap_link_format_get(&sock, sock_urlpath(url), cb, arg); + nanocoap_sock_close(&sock); + + return res; +} From f06c76348ae80017090e35d155627d386ce80675 Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Tue, 24 May 2022 16:25:57 +0200 Subject: [PATCH 2/2] sys/shell: ncget: make use of nanocoap_link_format_get() --- sys/shell/Makefile.dep | 1 + sys/shell/cmds/nanocoap_vfs.c | 52 ++++++++++++----------------------- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/sys/shell/Makefile.dep b/sys/shell/Makefile.dep index 65fffb0115ba2..708cfa7b2b491 100644 --- a/sys/shell/Makefile.dep +++ b/sys/shell/Makefile.dep @@ -216,6 +216,7 @@ endif ifneq (,$(filter shell_cmd_nanocoap_vfs,$(USEMODULE))) USEMODULE += nanocoap_vfs USEMODULE += vfs_util + USEMODULE += nanocoap_link_format endif ifneq (,$(filter shell_cmd_netstats_neighbor,$(USEMODULE))) USEMODULE += netstats_neighbor diff --git a/sys/shell/cmds/nanocoap_vfs.c b/sys/shell/cmds/nanocoap_vfs.c index 9585802894df6..4bd0b87b2713c 100644 --- a/sys/shell/cmds/nanocoap_vfs.c +++ b/sys/shell/cmds/nanocoap_vfs.c @@ -23,8 +23,10 @@ #include #include +#include "net/nanocoap/link_format.h" #include "net/nanocoap_sock.h" #include "net/nanocoap_vfs.h" + #include "shell.h" #include "vfs_default.h" #include "vfs_util.h" @@ -48,54 +50,34 @@ static bool _is_dir(const char *url) return url[len - 1] == '/'; } -static int _print_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more) +static int _resource_cb(char *entry, void *ctx) { - (void)arg; - (void)offset; + (void)ctx; - write(STDOUT_FILENO, buf, len); - if (!more) { - puts(""); + char *start = strchr(entry, '<'); + if (start) { + char *end = strchr(entry, '>'); + *end = '\0'; + entry = start + 1; } + puts(entry); return 0; } -static int _print_dir_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more) +static int _print_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more) { + (void)arg; (void)offset; - (void)more; - - struct dir_list_ctx *ctx = arg; - - char *end = (char *)buf + len; - for (char *c = (char *)buf; c < end; ++c) { - if (ctx->cur) { - if (*c == '>' || ctx->cur == ctx->end) { - *ctx->cur = 0; - puts(ctx->buf); - ctx->cur = NULL; - } else { - *ctx->cur++ = *c; - } - } else if (*c == '<') { - ctx->cur = ctx->buf; - } + + write(STDOUT_FILENO, buf, len); + if (!more) { + puts(""); } return 0; } -static int _print_dir(const char *url, char *buf, size_t len) -{ - struct dir_list_ctx ctx = { - .buf = buf, - .end = buf + len, - }; - return nanocoap_get_blockwise_url(url, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT, - _print_dir_cb, &ctx); -} - static int _nanocoap_get_handler(int argc, char **argv) { int res; @@ -109,7 +91,7 @@ static int _nanocoap_get_handler(int argc, char **argv) } if (_is_dir(url) && argc < 3) { - res = _print_dir(url, buffer, sizeof(buffer)); + res = nanocoap_link_format_get_url(url, _resource_cb, NULL); if (res) { printf("Request failed: %s\n", strerror(-res)); }