diff --git a/makefiles/dependency_resolution.inc.mk b/makefiles/dependency_resolution.inc.mk index 7c33021e56..ecea9e1086 100644 --- a/makefiles/dependency_resolution.inc.mk +++ b/makefiles/dependency_resolution.inc.mk @@ -80,4 +80,17 @@ else "don't run this on public networks!$(COLOR_RESET)" 1>&2) endif endif + + # Warn about STDIO UDP + ifneq (,$(filter stdio_udp,$(USEMODULE))) + ifneq (1,$(I_UNDERSTAND_THAT_STDIO_UDP_IS_INSECURE)) + $(shell $(COLOR_ECHO) "$(COLOR_RED)stdio via UDP will be started automatically,"\ + "make sure you understand why this almost certainly"\ + "is a REALLY BAD idea before proceeding!$(COLOR_RESET)" 1>&2) + $(error I_UNDERSTAND_THAT_STDIO_UDP_IS_INSECURE must be set to 1 to proceed) + else + $(shell $(COLOR_ECHO) "$(COLOR_YELLOW)stdio via UDP will be started automatically,"\ + "don't run this on public networks!$(COLOR_RESET)" 1>&2) + endif + endif endif diff --git a/makefiles/stdio.inc.mk b/makefiles/stdio.inc.mk index 2b53f54107..e641523145 100644 --- a/makefiles/stdio.inc.mk +++ b/makefiles/stdio.inc.mk @@ -8,6 +8,7 @@ STDIO_MODULES = \ stdio_rtt \ stdio_semihosting \ stdio_uart \ + stdio_udp \ stdio_telnet \ stdio_tinyusb_cdc_acm \ stdio_usb_serial_jtag \ @@ -75,6 +76,10 @@ ifneq (,$(filter stdio_telnet,$(USEMODULE))) USEMODULE += telnet endif +ifneq (,$(filter stdio_udp,$(USEMODULE))) + USEMODULE += sock_udp +endif + # enable stdout buffering for modules that benefit from sending out buffers in larger chunks ifneq (,$(filter picolibc,$(USEMODULE))) ifneq (,$(filter stdio_cdc_acm stdio_ethos slipdev_stdio stdio_semihosting,$(USEMODULE))) diff --git a/sys/stdio_udp/Makefile b/sys/stdio_udp/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/sys/stdio_udp/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/sys/stdio_udp/doc.txt b/sys/stdio_udp/doc.txt new file mode 100644 index 0000000000..b4a12b439a --- /dev/null +++ b/sys/stdio_udp/doc.txt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 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. + */ + +/** + * @defgroup sys_stdio_udp STDIO over UDP + * @ingroup sys_stdio + * @brief STDIO over UDP implementation + * + * This file implements STDIO via a UDP that can be used with e.g. netcat: + * + * nc -u fe80::7837:fcff:fe7d:1aaf%tapbr0 2323 + * + * It can be enabled with + * + * USEMODULE += stdio_udp + * + * and will listen on `CONFIG_STDIO_UDP_PORT` for incoming connections. + * + * You will also have to set `I_UNDERSTAND_THAT_STDIO_UDP_IS_INSECURE = 1` to + * acknowledge that you will only use this for debugging in an isolated network. + * + * @warning This is entirely unsecured, only use this for debugging in + * an isolated network! + */ diff --git a/sys/stdio_udp/stdio_udp.c b/sys/stdio_udp/stdio_udp.c new file mode 100644 index 0000000000..0a4ad30d66 --- /dev/null +++ b/sys/stdio_udp/stdio_udp.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 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 sys_stdio_udp + * @{ + * + * @file + * @brief STDIO over UDP implementation + * + * @author Benjamin Valentin + * + * @} + */ + +#include + +#include "macros/utils.h" +#include "net/sock/udp.h" + +#ifndef CONFIG_STDIO_UDP_PORT +#define CONFIG_STDIO_UDP_PORT 2323 +#endif + +#ifndef CONFIG_STDIO_UDP_RX_BUF_LEN +#define CONFIG_STDIO_UDP_RX_BUF_LEN 64 +#endif + +static sock_udp_t sock; +static sock_udp_ep_t remote; + +void stdio_init(void) +{ + const sock_udp_ep_t local = { + .family = AF_INET6, + .netif = SOCK_ADDR_ANY_NETIF, + .port = CONFIG_STDIO_UDP_PORT, + }; + + sock_udp_create(&sock, &local, NULL, 0); +} + +ssize_t stdio_read(void* buffer, size_t count) +{ + static uint8_t rx_buf[CONFIG_STDIO_UDP_RX_BUF_LEN]; + static uint8_t *pos; + static size_t left; + + /* shell will only read one byte at a time, so we buffer the input */ + if (left == 0) { + int res = sock_udp_recv(&sock, rx_buf, sizeof(rx_buf), + SOCK_NO_TIMEOUT, &remote); + if (res > 0) { + left = res; + pos = rx_buf; + } else { + return res; + } + } + + count = MIN(left, count); + memcpy(buffer, pos, count); + + left -= count; + pos += count; + + return count; +} + +ssize_t stdio_write(const void* buffer, size_t len) +{ + if (remote.port == 0) { + return -ENOTCONN; + } + if (len == 0) { + return 0; + } + + return sock_udp_send(&sock, buffer, len, &remote); +}