From 3e9514927ac4dbeafb8ac00e0dd556fa31090803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Mon, 8 Apr 2024 11:22:00 +0200 Subject: [PATCH 1/3] examples/gcoap[_dtls]: input full URI for request and proxy --- examples/gcoap/Makefile | 1 + examples/gcoap/client.c | 232 +++++++++++++++-------------------- examples/gcoap_dtls/Makefile | 1 + 3 files changed, 102 insertions(+), 132 deletions(-) diff --git a/examples/gcoap/Makefile b/examples/gcoap/Makefile index 89805fe1cebc..eb88a86f3eba 100644 --- a/examples/gcoap/Makefile +++ b/examples/gcoap/Makefile @@ -53,6 +53,7 @@ USEMODULE += random # Add also the shell, some shell commands USEMODULE += shell USEMODULE += shell_cmds_default +USEMODULE += uri_parser USEMODULE += ps # Comment this out to disable code in RIOT that does safety checking diff --git a/examples/gcoap/client.c b/examples/gcoap/client.c index 9af427f24159..52b2203489c2 100644 --- a/examples/gcoap/client.c +++ b/examples/gcoap/client.c @@ -16,22 +16,21 @@ * @author Ken Bannister * @author Hauke Petersen * @author Hendrik van Essen + * @author Fabian Hüßler * * @} */ +#include #include #include #include #include -#include - -#include "fmt.h" #include "net/gcoap.h" #include "net/sock/util.h" -#include "net/utils.h" #include "od.h" +#include "uri_parser.h" #include "gcoap_example.h" @@ -42,15 +41,17 @@ #include "net/dsm.h" #endif -static bool _proxied = false; +#ifndef CONFIG_URI_MAX +#define CONFIG_URI_MAX 128 +#endif + static sock_udp_ep_t _proxy_remote; -static char proxy_uri[64]; +static char _proxy_uri[CONFIG_URI_MAX]; -/* Retain request path to re-request if response includes block. User must not +/* Retain request URI to re-request if response includes block. User must not * start a new request (with a new path) until any blockwise transfer * completes or times out. */ -#define _LAST_REQ_PATH_MAX (64) -static char _last_req_path[_LAST_REQ_PATH_MAX]; +static char _last_req_uri[CONFIG_URI_MAX]; /* whether this node is currently observing a resource as a client */ static bool observing = false; @@ -63,6 +64,10 @@ static size_t obs_req_tkl = 0; uint16_t req_count = 0; +static gcoap_socket_type_t _get_tl(const char *uri); +static ssize_t _send(uint8_t *buf, size_t len, const sock_udp_ep_t *remote, + void *ctx, gcoap_socket_type_t tl); + /* * Response callback. */ @@ -120,18 +125,19 @@ static void _resp_handler(const gcoap_request_memo_t *memo, coap_pkt_t* pdu, if (coap_get_block2(pdu, &block)) { if (block.more) { unsigned msg_type = coap_get_type(pdu); - if (block.blknum == 0 && !strlen(_last_req_path)) { + if (block.blknum == 0 && !strlen(_last_req_uri)) { puts("Path too long; can't complete blockwise"); return; } - - if (_proxied) { + uri_parser_result_t urip; + uri_parser_process(&urip, _last_req_uri, strlen(_last_req_uri)); + if (*_proxy_uri) { gcoap_req_init(pdu, (uint8_t *)pdu->hdr, CONFIG_GCOAP_PDU_BUF_SIZE, COAP_METHOD_GET, NULL); } else { gcoap_req_init(pdu, (uint8_t *)pdu->hdr, CONFIG_GCOAP_PDU_BUF_SIZE, - COAP_METHOD_GET, _last_req_path); + COAP_METHOD_GET, urip.path); } if (msg_type == COAP_TYPE_ACK) { @@ -140,14 +146,13 @@ static void _resp_handler(const gcoap_request_memo_t *memo, coap_pkt_t* pdu, block.blknum++; coap_opt_add_block2_control(pdu, &block); - if (_proxied) { - coap_opt_add_proxy_uri(pdu, _last_req_path); + if (*_proxy_uri) { + coap_opt_add_proxy_uri(pdu, urip.scheme); } int len = coap_opt_finish(pdu, COAP_OPT_FINISH_NONE); - gcoap_req_send((uint8_t *)pdu->hdr, len, remote, - _resp_handler, memo->context, - GCOAP_SOCKET_TYPE_UNDEF); + gcoap_socket_type_t tl = _get_tl(*_proxy_uri ? _proxy_uri : _last_req_uri); + _send((uint8_t *)pdu->hdr, len, remote, memo->context, tl); } else { puts("--- blockwise complete ---"); @@ -155,12 +160,21 @@ static void _resp_handler(const gcoap_request_memo_t *memo, coap_pkt_t* pdu, } } -static size_t _send(uint8_t *buf, size_t len, sock_udp_ep_t *remote) +static gcoap_socket_type_t _get_tl(const char *uri) { - size_t bytes_sent; + if (!strncmp(uri, "coaps", 5)) { + return GCOAP_SOCKET_TYPE_DTLS; + } + else if (!strncmp(uri, "coap", 4)) { + return GCOAP_SOCKET_TYPE_UDP; + } + return GCOAP_SOCKET_TYPE_UNDEF; +} - bytes_sent = gcoap_req_send(buf, len, remote, _resp_handler, NULL, - GCOAP_SOCKET_TYPE_UNDEF); +static ssize_t _send(uint8_t *buf, size_t len, const sock_udp_ep_t *remote, + void *ctx, gcoap_socket_type_t tl) +{ + ssize_t bytes_sent = gcoap_req_send(buf, len, remote, _resp_handler, ctx, tl); if (bytes_sent > 0) { req_count++; } @@ -169,24 +183,46 @@ static size_t _send(uint8_t *buf, size_t len, sock_udp_ep_t *remote) static int _print_usage(char **argv) { - printf("usage: %s \n", argv[0]); + printf("usage: %s [-c] [data]\n", argv[0]); + printf(" %s ping ://[:port]\n", argv[0]); + printf(" %s info\n", argv[0]); + printf(" %s proxy set ://[:port]\n", argv[0]); + printf(" %s proxy unset\n", argv[0]); + printf("Options\n"); + printf(" -c Send confirmably (defaults to non-confirmable)\n"); return 1; } -static int _addrstr2remote(const char *addr_str, sock_udp_ep_t *remote) +static int _uristr2remote(const char *uri, sock_udp_ep_t *remote, const char **path, + char *buf, size_t buf_len) { - if (sock_udp_name2ep(remote, addr_str) != 0) { + if (strlen(uri) >= buf_len) { + DEBUG_PUTS("URI too long"); + return 1; + } + uri_parser_result_t urip; + if (uri_parser_process(&urip, uri, strlen(uri))) { + DEBUG("'%s' is not a valid URI\n", uri); + return 1; + } + memcpy(buf, urip.host, urip.host_len); + buf[urip.host_len] = '\0'; + if (urip.port_str_len) { + strcat(buf, ":"); + strncat(buf, urip.port_str, urip.port_str_len); + buf[urip.host_len + 1 + urip.port_str_len] = '\0'; + } + if (sock_udp_name2ep(remote, buf) != 0) { + DEBUG("Could not resolve address '%s'\n", buf); return -1; } - if (remote->port == 0) { - if (IS_USED(MODULE_GCOAP_DTLS)) { - remote->port = CONFIG_GCOAPS_PORT; - } - else { - remote->port = CONFIG_GCOAP_PORT; - } + remote->port = !strncmp("coaps", urip.scheme, 5) ? CONFIG_GCOAPS_PORT : CONFIG_GCOAP_PORT; + } + if (path) { + *path = urip.path; } + strcpy(buf, uri); return 0; } @@ -202,8 +238,7 @@ int gcoap_cli_cmd(int argc, char **argv) sock_udp_ep_t remote; if (argc == 1) { - /* show help for main commands */ - return _print_usage(argv); + goto help; } if (strcmp(argv[1], "info") == 0) { @@ -222,20 +257,8 @@ int gcoap_cli_cmd(int argc, char **argv) printf(" CLI requests sent: %u\n", req_count); printf("CoAP open requests: %u\n", open_reqs); printf("Configured Proxy: "); - if (_proxied) { -#ifdef SOCK_HAS_IPV6 - char addrstr[IPV6_ADDR_MAX_STR_LEN]; -#else - char addrstr[IPV4_ADDR_MAX_STR_LEN]; -#endif - inet_ntop(_proxy_remote.family, &_proxy_remote.addr, addrstr, sizeof(addrstr)); - - if (_proxy_remote.family == AF_INET6) { - printf("[%s]:%u\n", addrstr, _proxy_remote.port); - } - else { - printf("%s:%u\n", addrstr, _proxy_remote.port); - } + if (*_proxy_uri) { + printf("%s\n", _proxy_uri); } else { puts("None"); @@ -244,42 +267,29 @@ int gcoap_cli_cmd(int argc, char **argv) } else if (strcmp(argv[1], "proxy") == 0) { if ((argc == 4) && (strcmp(argv[2], "set") == 0)) { - if (sock_udp_name2ep(&_proxy_remote, argv[3]) != 0) { + if (_uristr2remote(argv[3], &_proxy_remote, NULL, _proxy_uri, sizeof(_proxy_uri))) { puts("Could not set proxy"); return 1; } - - if (_proxy_remote.port == 0) { - if (IS_USED(MODULE_GCOAP_DTLS)) { - _proxy_remote.port = CONFIG_GCOAPS_PORT; - } - else { - _proxy_remote.port = CONFIG_GCOAP_PORT; - } - } - - _proxied = true; return 0; } if ((argc == 3) && (strcmp(argv[2], "unset") == 0)) { memset(&_proxy_remote, 0, sizeof(_proxy_remote)); - _proxied = false; + memset(_proxy_uri, 0, sizeof(_proxy_uri)); return 0; } - printf("usage: %s proxy set [:port]\n", argv[0]); - printf(" %s proxy unset\n", argv[0]); - return 1; + goto help; } /* if not 'info' and 'proxy', must be a method code or ping */ int code_pos = -1; - for (size_t i = 0; i < ARRAY_SIZE(method_codes); i++) { + for (size_t i = 0; i < ARRAY_SIZE(method_codes) && code_pos == -1; i++) { if (strcmp(argv[1], method_codes[i]) == 0) { code_pos = i; } } if (code_pos == -1) { - return _print_usage(argv); + goto help; } /* parse options */ @@ -314,50 +324,12 @@ int gcoap_cli_cmd(int argc, char **argv) msg_type = COAP_TYPE_CON; apos++; } - - if (((argc == apos + 1) && (code_pos == 0)) || /* ping */ - ((argc == apos + 2) && (code_pos == 1)) || /* get */ - ((argc == apos + 2 || - argc == apos + 3) && (code_pos > 1))) { /* post or put */ - - /* get unproxied endpoint from address string */ - if (_addrstr2remote(argv[apos], &remote)) { - printf("'%s' is not a valid address\n", argv[apos]); - return _print_usage(argv); - } - - char *uri = NULL; - int uri_len = 0; - if (code_pos) { - uri = argv[apos+1]; - uri_len = strlen(argv[apos+1]); + if (apos < argc) { + const char *path; + if (_uristr2remote(argv[apos++], &remote, &path, _last_req_uri, sizeof(_last_req_uri))) { + puts("Could not parse URI"); + goto help; } - - if (uri && ((uri_len <= 0) || (uri[0] != '/'))) { - puts("ERROR: URI-Path must start with a \"/\""); - return _print_usage(argv); - } - - if (_proxied) { -#ifdef SOCK_HAS_IPV6 - char addrstr[IPV6_ADDR_MAX_STR_LEN]; -#else - char addrstr[IPV4_ADDR_MAX_STR_LEN]; -#endif - inet_ntop(remote.family, &remote.addr, addrstr, sizeof(addrstr)); - - if (remote.family == AF_INET6) { - uri_len = snprintf(proxy_uri, sizeof(proxy_uri), "coap://[%s]:%d%s", - addrstr, remote.port, uri); - } - else { - uri_len = snprintf(proxy_uri, sizeof(proxy_uri), "coap://%s:%d%s", - addrstr, remote.port, uri); - } - - uri = proxy_uri; - } - gcoap_req_init(&pdu, buf, CONFIG_GCOAP_PDU_BUF_SIZE, code_pos, NULL); if (observe) { @@ -379,32 +351,30 @@ int gcoap_cli_cmd(int argc, char **argv) coap_opt_add_uint(&pdu, COAP_OPT_OBSERVE, obs_value); } - if (!_proxied) { - /* add uri path option separately + if (!*_proxy_uri) { + /* If the request is not a ping, add uri path option separately * (options must be added in order) */ - coap_opt_add_uri_path(&pdu, uri); + if (path) { + coap_opt_add_uri_path(&pdu, path); + } } coap_hdr_set_type(pdu.hdr, msg_type); - memset(_last_req_path, 0, _LAST_REQ_PATH_MAX); - if (uri_len < _LAST_REQ_PATH_MAX) { - memcpy(_last_req_path, uri, uri_len); - } - - size_t paylen = (argc == apos + 3) ? strlen(argv[apos+2]) : 0; - if (paylen) { + size_t paylen = 0; + if (apos < argc) { coap_opt_add_format(&pdu, COAP_FORMAT_TEXT); + paylen = strlen(argv[apos]); } - if (_proxied) { - coap_opt_add_proxy_uri(&pdu, uri); + if (*_proxy_uri) { + coap_opt_add_proxy_uri(&pdu, _last_req_uri); } if (paylen) { len = coap_opt_finish(&pdu, COAP_OPT_FINISH_PAYLOAD); if (pdu.payload_len >= paylen) { - memcpy(pdu.payload, argv[apos+2], paylen); + memcpy(pdu.payload, argv[apos++], paylen); len += paylen; } else { @@ -418,7 +388,13 @@ int gcoap_cli_cmd(int argc, char **argv) printf("gcoap_cli: sending msg ID %u, %" PRIuSIZE " bytes\n", coap_get_id(&pdu), len); - if (!_send(&buf[0], len, _proxied ? &_proxy_remote : &remote)) { + gcoap_socket_type_t tl = _get_tl(_last_req_uri); + sock_udp_ep_t *rem = &remote; + if (*_proxy_uri) { + rem = &_proxy_remote; + tl = _get_tl(_proxy_uri); + } + if (_send(&buf[0], len, rem, NULL, tl) <= 0) { puts("gcoap_cli: msg send failed"); } else { @@ -432,14 +408,6 @@ int gcoap_cli_cmd(int argc, char **argv) } return 0; } - else { - printf("usage: %s [-c] [:port] [data]\n", - argv[0]); - printf(" %s ping [:port]\n", argv[0]); - printf("Options\n"); - printf(" -c Send confirmably (defaults to non-confirmable)\n"); - return 1; - } - +help: return _print_usage(argv); } diff --git a/examples/gcoap_dtls/Makefile b/examples/gcoap_dtls/Makefile index ce1ee3396b36..1e3d86f525ec 100644 --- a/examples/gcoap_dtls/Makefile +++ b/examples/gcoap_dtls/Makefile @@ -54,6 +54,7 @@ USEMODULE += random USEMODULE += shell USEMODULE += shell_cmds_default USEMODULE += ps +USEMODULE += uri_parser # Comment this out to disable code in RIOT that does safety checking # which is not needed in a production environment but helps in the From a5b6c4c051a07ace1c2e96a5e353a86a73ce9501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Mon, 8 Apr 2024 11:30:30 +0200 Subject: [PATCH 2/3] gcoap/forward_proxy: minor fix and cleanup --- .../application_layer/gcoap/forward_proxy.c | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/sys/net/application_layer/gcoap/forward_proxy.c b/sys/net/application_layer/gcoap/forward_proxy.c index 4e863f8beb00..ddb95b59c0ee 100644 --- a/sys/net/application_layer/gcoap/forward_proxy.c +++ b/sys/net/application_layer/gcoap/forward_proxy.c @@ -77,6 +77,17 @@ gcoap_listener_t forward_proxy_listener = { _request_matcher_forward_proxy }; +static void _cep_set_timeout(client_ep_t *cep, ztimer_t *timer, uint32_t timeout_ms, + event_handler_t handler) +{ + assert(!ztimer_is_set(ZTIMER_MSEC, timer)); + timer->callback = gcoap_forward_proxy_post_event; + timer->arg = &cep->event; + cep->event.handler = handler; + ztimer_set(ZTIMER_MSEC, timer, timeout_ms); +} + + void gcoap_forward_proxy_init(void) { gcoap_register_listener(&forward_proxy_listener); @@ -205,6 +216,9 @@ static bool _parse_endpoint(sock_udp_ep_t *remote, if (urip->port != 0) { remote->port = urip->port; } + else if (!strncmp(urip->scheme, "coaps", 5)) { + remote->port = COAPS_PORT; + } else { remote->port = COAP_PORT; } @@ -424,11 +438,8 @@ static int _gcoap_forward_proxy_via_coap(coap_pkt_t *client_pkt, } if (coap_get_type(client_pkt) == COAP_TYPE_CON) { - client_ep->empty_ack_timer.callback = gcoap_forward_proxy_post_event; - client_ep->empty_ack_timer.arg = &client_ep->event; - client_ep->event.handler = _send_empty_ack; - ztimer_set(ZTIMER_MSEC, &client_ep->empty_ack_timer, - CONFIG_GCOAP_FORWARD_PROXY_EMPTY_ACK_MS); + _cep_set_timeout(client_ep, &client_ep->empty_ack_timer, + CONFIG_GCOAP_FORWARD_PROXY_EMPTY_ACK_MS, _send_empty_ack); } unsigned token_len = coap_get_token_len(client_pkt); From 7e97ee857195ce5874c3b75031cefd3b08dccc98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20H=C3=BC=C3=9Fler?= Date: Tue, 14 May 2024 08:54:41 +0200 Subject: [PATCH 3/3] examples/gcoap: adjust README after taking URI as input --- examples/gcoap/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/gcoap/README.md b/examples/gcoap/README.md index 01a11af2a0f2..bc05b43218f6 100644 --- a/examples/gcoap/README.md +++ b/examples/gcoap/README.md @@ -62,15 +62,15 @@ The port defaults to 5683 for the `gcoap` and to 5684 for the `gcoap_dtls` examp - For IPv6 setup (network interface can be omitted) - > coap get [fe80::d8b8:65ff:feee:121b%6]:5683 /.well-known/core + > coap get coap://[fe80::d8b8:65ff:feee:121b%6]:5683/.well-known/core - For IPv4 setup - > coap get 192.168.2.135:5683 /.well-known/core + > coap get coap://192.168.2.135:5683/.well-known/core - When including the module `sock_dns` for domain resolution - > coap get example.com:5683 /.well-known/core + > coap get coap://example.com:5683/.well-known/core CLI output: