Skip to content

Commit

Permalink
Merge pull request #16723 from benpicco/gnrc_sock_tcp-telnet
Browse files Browse the repository at this point in the history
sys/net/application_layer: add telnet server module & example
  • Loading branch information
maribu authored Mar 2, 2022
2 parents c9cc74f + 2e46e4a commit fbe73b7
Show file tree
Hide file tree
Showing 16 changed files with 747 additions and 0 deletions.
51 changes: 51 additions & 0 deletions examples/telnet_server/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# name of your application
APPLICATION = telnet_server

# If no BOARD is found in the environment, use this default:
BOARD ?= native

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

# Include packages that pull up and auto-init the link layer.
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
USEMODULE += netdev_default
USEMODULE += auto_init_gnrc_netif
# Activate ICMPv6 error messages
USEMODULE += gnrc_icmpv6_error
# Specify the mandatory networking modules for IPv6
USEMODULE += gnrc_ipv6_default
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo
USEMODULE += netutils
# Add also the shell, some shell commands
USEMODULE += shell
USEMODULE += shell_commands
USEMODULE += ps
# Include the telnet server
USEMODULE += stdio_telnet

# Enable faster re-connects
CFLAGS += -DCONFIG_GNRC_TCP_EXPERIMENTAL_DYN_MSL_EN=1

# Acknowledge that telnet is bad
I_UNDERSTAND_THAT_TELNET_IS_INSECURE = 1

# enable debug output via UART
FEATURES_OPTIONAL += periph_uart

# Optionally include DNS support. This includes resolution of names at an
# upstream DNS server and the handling of RDNSS options in Router Advertisements
# to auto-configure that upstream DNS server.
# USEMODULE += sock_dns # include DNS client
# USEMODULE += gnrc_ipv6_nib_dns # include RDNSS option handling

# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1

include $(RIOTBASE)/Makefile.include

# Set a custom channel if needed
include $(RIOTMAKE)/default-radio-settings.inc.mk
37 changes: 37 additions & 0 deletions examples/telnet_server/Makefile.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-mega2560 \
arduino-nano \
arduino-uno \
atmega1284p \
atmega328p \
atmega328p-xplained-mini \
atxmega-a3bu-xplained \
bluepill-stm32f030c8 \
derfmega128 \
i-nucleo-lrwan1 \
mega-xplained \
microduino-corerf \
msb-430 \
msb-430h \
nucleo-f030r8 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l011k4 \
nucleo-l031k6 \
nucleo-l053r8 \
samd10-xmini \
stk3200 \
slstk3400a \
stm32f030f4-demo \
stm32f0discovery \
stm32g0316-disco \
stm32l0538-disco \
telosb \
waspmote-pro \
z1 \
zigduino \
#
104 changes: 104 additions & 0 deletions examples/telnet_server/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright (C) 2021 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 examples
* @{
*
* @file
* @brief Example application for demonstrating the RIOT telnet server
*
* @author Benjamin Valentin <[email protected]>
*
* @}
*/

#include <stdio.h>

#include "net/ipv6/addr.h"
#include "net/gnrc.h"
#include "net/gnrc/netif.h"
#include "net/telnet.h"
#include "shell.h"
#include "msg.h"

#define MAIN_QUEUE_SIZE (8)
static msg_t _main_msg_queue[MAIN_QUEUE_SIZE];

static void _print_addr(void)
{
gnrc_netif_t *netif = NULL;
while ((netif = gnrc_netif_iter(netif))) {
ipv6_addr_t ipv6_addrs[CONFIG_GNRC_NETIF_IPV6_ADDRS_NUMOF];
int res = gnrc_netapi_get(netif->pid, NETOPT_IPV6_ADDR, 0, ipv6_addrs,
sizeof(ipv6_addrs));

if (res < 0) {
continue;
}
for (unsigned i = 0; i < (unsigned)(res / sizeof(ipv6_addr_t)); i++) {
char ipv6_addr[IPV6_ADDR_MAX_STR_LEN];

ipv6_addr_to_str(ipv6_addr, &ipv6_addrs[i], IPV6_ADDR_MAX_STR_LEN);
printf("My address is %s\n", ipv6_addr);
}
}
}

static void _print_motd(void)
{
puts("RIOT telnet example application");

puts("╔═══════════════════════════════════════════════════╗");
puts("║telnet is entirely unencrypted and unauthenticated.║");
puts("║Do not use this on public networks. ║");
puts("╚═══════════════════════════════════════════════════╝");
}

void telnet_cb_pre_connected(sock_tcp_t *sock)
{
sock_tcp_ep_t ep;
char addr_str[IPV6_ADDR_MAX_STR_LEN];

sock_tcp_get_local(sock, &ep);
ipv6_addr_to_str(addr_str, (ipv6_addr_t *)ep.addr.ipv6, sizeof(addr_str));

printf("%s connected\n", addr_str);
}

void telnet_cb_disconneced(void)
{
puts("disconnected");
}

void telnet_cb_connected(sock_tcp_t *sock)
{
(void)sock;
_print_motd();
}

int main(void)
{
/* we need a message queue for the thread running the shell in order to
* receive potentially fast incoming networking packets */
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);

_print_motd();

/* print address so we can connect to it */
_print_addr();

/* start shell */
printf("All up, awaiting connection on port %u\n", CONFIG_TELNET_PORT);
printf("Local shell disabled");
char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);

/* should be never reached */
return 0;
}
13 changes: 13 additions & 0 deletions makefiles/dependency_resolution.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,17 @@ else
$(shell $(COLOR_ECHO) "$(COLOR_RED)Deprecated modules are in use:$(COLOR_RESET)"\
"$(DEPRECATED_MODULES_USED)" 1>&2)
endif

# Warn about telnet
ifneq (,$(filter auto_init_telnet,$(USEMODULE)))
ifneq (1,$(I_UNDERSTAND_THAT_TELNET_IS_INSECURE))
$(shell $(COLOR_ECHO) "$(COLOR_RED)Telnet 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_TELNET_IS_INSECURE must be set to 1 to proceed)
else
$(shell $(COLOR_ECHO) "$(COLOR_YELLOW)Telnet will be started automatically,"\
"don't run this on public networks!$(COLOR_RESET)" 1>&2)
endif
endif
endif
1 change: 1 addition & 0 deletions makefiles/pseudomodules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ PSEUDOMODULES += stdio_cdc_acm
PSEUDOMODULES += stdio_ethos
PSEUDOMODULES += stdio_nimble_debug
PSEUDOMODULES += stdio_uart_rx
PSEUDOMODULES += stdio_telnet
PSEUDOMODULES += stm32_eth
PSEUDOMODULES += stm32_eth_auto
PSEUDOMODULES += stm32_eth_link_up
Expand Down
6 changes: 6 additions & 0 deletions makefiles/stdio.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ STDIO_MODULES = \
stdio_rtt \
stdio_semihosting \
stdio_uart \
stdio_telnet \
#

ifneq (,$(filter newlib picolibc,$(USEMODULE)))
Expand Down Expand Up @@ -63,6 +64,11 @@ ifneq (,$(filter stdio_semihosting,$(USEMODULE)))
FEATURES_REQUIRED_ANY += cpu_core_cortexm|arch_riscv
endif

ifneq (,$(filter stdio_telnet,$(USEMODULE)))
DEFAULT_MODULE += auto_init_telnet
USEMODULE += telnet
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)))
Expand Down
3 changes: 3 additions & 0 deletions sys/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ endif
ifneq (,$(filter cipher_modes,$(USEMODULE)))
DIRS += crypto/modes
endif
ifneq (,$(filter telnet,$(USEMODULE)))
DIRS += net/application_layer/telnet
endif
ifneq (,$(filter constfs,$(USEMODULE)))
DIRS += fs/constfs
endif
Expand Down
5 changes: 5 additions & 0 deletions sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ ifneq (,$(filter sema_inv,$(USEMODULE)))
USEMODULE += atomic_utils
endif

ifneq (,$(filter telnet,$(USEMODULE)))
USEMODULE += pipe
USEMODULE += sock_tcp
endif

ifneq (,$(filter luid,$(USEMODULE)))
FEATURES_OPTIONAL += periph_cpuid
endif
Expand Down
6 changes: 6 additions & 0 deletions sys/auto_init/auto_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ void auto_init(void)
gnrc_ipv6_auto_subnets_init();
}

if (IS_USED(MODULE_AUTO_INIT_TELNET)) {
LOG_DEBUG("auto_init TELNET server\n");
extern void telnet_server_start(void);
telnet_server_start();
}

if (IS_USED(MODULE_AUTO_INIT_MULTIMEDIA)) {
LOG_DEBUG("auto_init MULTIMEDIA\n");
if (IS_USED(MODULE_DFPLAYER)) {
Expand Down
95 changes: 95 additions & 0 deletions sys/include/net/telnet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2021 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 net_telnet basic Telnet server implementation
* @ingroup net_ipv6
* @brief Telnet server functions
* @{
*
* @file
* @brief minimal Telnet server ([RFC 854](https://tools.ietf.org/html/rfc854)) implementation
* @note This implementation only supports a single client and no options.
*
* @warning Telnet is entirely unencrypted! Do not use it on public networks.
* This is intended to aid debugging on networks that are isolated from the Internet.
*
* @author Benjamin Valentin <[email protected]>
*/
#ifndef NET_TELNET_H
#define NET_TELNET_H

#include "net/sock/tcp.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief The port for the Telnet server to listen on
*/
#ifndef CONFIG_TELNET_PORT
#define CONFIG_TELNET_PORT (23)
#endif

/**
* @brief Start the Telnet server thread
*
* @return 0 on success, error otherwise
*/
int telnet_server_start(void);

/**
* @brief Write data to the telnet client
*
* @param[in] buffer The buffer to send to the client
* @param[in] len The length of the buffer
*
* @return 0 on success, error otherwise
*/
int telnet_server_write(const void* buffer, size_t len);

/**
* @brief Read data from the telnet client, will block until data is available.
*
* @param[out] buffer The buffer to write data from the client
* @param[in] count Number of bytes to read
*
* @return number of bytes read, error otherwise
*/
int telnet_server_read(void* buffer, size_t count);

/**
* @brief Callback function that gets called when a telnet client connects
* but before stdio is redirected.
*
* @param[in] sock Socket of the client that just connected
* only use with @ref sock_tcp_get_local
*/
void telnet_cb_pre_connected(sock_tcp_t *sock);

/**
* @brief Callback function that gets called when a telnet client connects
* after stdio is redirected.
*
* @param[in] sock Socket of the client that just connected
* only use with @ref sock_tcp_get_local
*/
void telnet_cb_connected(sock_tcp_t *sock);

/**
* @brief Callback function that gets called after a telnet client disconnected.
*/
void telnet_cb_disconneced(void);

#ifdef __cplusplus
}
#endif

#endif /* NET_TELNET_H */
/** @} */
6 changes: 6 additions & 0 deletions sys/net/application_layer/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ rsource "asymcute/Kconfig"
rsource "emcute/Kconfig"

endmenu # MQTT-SN

menu "Telnet"

rsource "telnet/Kconfig"

endmenu # Telnet
Loading

0 comments on commit fbe73b7

Please sign in to comment.