summaryrefslogtreecommitdiffstats
path: root/cpukit/telnetd/telnetd.c
diff options
context:
space:
mode:
authorVijay Kumar Banerjee <vijay@rtems.org>2021-04-13 11:57:21 -0600
committerVijay Kumar Banerjee <vijay@rtems.org>2021-04-13 11:57:21 -0600
commit650ac700664238b842f13523cc8b1604d24b1794 (patch)
treecdd0d85dc8fedd8759cfbbbc829280dece7a17b0 /cpukit/telnetd/telnetd.c
parentdisp_hcms29xx.c: Unused value (CID #1399752) (diff)
downloadrtems-650ac700664238b842f13523cc8b1604d24b1794.tar.bz2
Revert "cpukit: Remove telnetd"
This reverts commit 3299dda2454a8847c670a732f6c12ef1f2cc5dd0.
Diffstat (limited to 'cpukit/telnetd/telnetd.c')
-rw-r--r--cpukit/telnetd/telnetd.c459
1 files changed, 459 insertions, 0 deletions
diff --git a/cpukit/telnetd/telnetd.c b/cpukit/telnetd/telnetd.c
new file mode 100644
index 0000000000..b8adec297a
--- /dev/null
+++ b/cpukit/telnetd/telnetd.c
@@ -0,0 +1,459 @@
+/***********************************************************/
+/*
+ *
+ * The telnet DAEMON
+ *
+ * Author: 17,may 2001
+ *
+ * WORK: fernando.ruiz@ctv.es
+ * HOME: correo@fernando-ruiz.com
+ *
+ * After start the net you can start this daemon.
+ * It uses the previously inited pseudo-terminales (pty.c)
+ * getting a new terminal with getpty(). This function
+ * gives a terminal name passing a opened socket like parameter.
+ *
+ * With register_telnetd() you add a new command in the shell to start
+ * this daemon interactively. (Login in /dev/console of course)
+ *
+ * Sorry but OOB is not still implemented. (This is the first version)
+ *
+ * Till Straumann <strauman@slac.stanford.edu>
+ * - made the 'shell' interface more generic, i.e. it is now
+ * possible to have 'telnetd' run an arbitrary 'shell'
+ * program.
+ *
+ * Copyright (c) 2009, 2018 embedded brains GmbH and others.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * D-82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <rtems.h>
+#include <rtems/pty.h>
+#include <rtems/shell.h>
+#include <rtems/telnetd.h>
+#include <rtems/thread.h>
+#include <rtems/userenv.h>
+
+#ifdef RTEMS_NETWORKING
+#include <rtems/rtems_bsdnet.h>
+#endif
+
+#define TELNETD_EVENT_SUCCESS RTEMS_EVENT_0
+
+#define TELNETD_EVENT_ERROR RTEMS_EVENT_1
+
+typedef struct telnetd_context telnetd_context;
+
+typedef struct telnetd_session {
+ rtems_pty_context pty;
+ char peername[16];
+ telnetd_context *ctx;
+ rtems_id task_id;
+ LIST_ENTRY(telnetd_session) link;
+} telnetd_session;
+
+struct telnetd_context {
+ rtems_telnetd_config_table config;
+ int server_socket;
+ rtems_id task_id;
+ rtems_mutex mtx;
+ LIST_HEAD(, telnetd_session) free_sessions;
+ telnetd_session sessions[RTEMS_ZERO_LENGTH_ARRAY];
+};
+
+typedef union {
+ struct sockaddr_in sin;
+ struct sockaddr sa;
+} telnetd_address;
+
+RTEMS_NO_RETURN static void telnetd_session_fatal_error(
+ const telnetd_context *ctx
+)
+{
+ (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_ERROR);
+ rtems_task_exit();
+}
+
+static bool telnetd_login(telnetd_context *ctx, telnetd_session *session)
+{
+ bool success;
+
+ if (ctx->config.login_check == NULL) {
+ return true;
+ }
+
+ success = rtems_shell_login_prompt(
+ stdin,
+ stderr,
+ session->pty.name,
+ ctx->config.login_check
+ );
+
+ if (!success) {
+ syslog(
+ LOG_AUTHPRIV | LOG_WARNING,
+ "telnetd: too many wrong passwords entered from %s",
+ session->peername
+ );
+ }
+
+ return success;
+}
+
+static void telnetd_session_task(rtems_task_argument arg)
+{
+ rtems_status_code sc;
+ telnetd_session *session;
+ telnetd_context *ctx;
+ const char *path;
+
+ session = (telnetd_session *) arg;
+ ctx = session->ctx;
+
+ sc = rtems_libio_set_private_env();
+ if (sc != RTEMS_SUCCESSFUL) {
+ telnetd_session_fatal_error(ctx);
+ }
+
+ path = rtems_pty_get_path(&session->pty);
+
+ stdin = fopen(path, "r+");
+ if (stdin == NULL) {
+ telnetd_session_fatal_error(ctx);
+ }
+
+ stdout = fopen(path, "r+");
+ if (stdout == NULL) {
+ telnetd_session_fatal_error(ctx);
+ }
+
+ stderr = fopen(path, "r+");
+ if (stderr == NULL) {
+ telnetd_session_fatal_error(ctx);
+ }
+
+ (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_SUCCESS);
+
+ while (true) {
+ rtems_event_set events;
+
+ (void)rtems_event_system_receive(
+ RTEMS_EVENT_SYSTEM_SERVER,
+ RTEMS_WAIT | RTEMS_EVENT_ALL,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+
+ syslog(
+ LOG_DAEMON | LOG_INFO,
+ "telnetd: accepted connection from %s on %s",
+ session->peername,
+ path
+ );
+
+ if (telnetd_login(ctx, session)) {
+ (*ctx->config.command)(session->pty.name, ctx->config.arg);
+ }
+
+ syslog(
+ LOG_DAEMON | LOG_INFO,
+ "telnetd: releasing connection from %s on %s",
+ session->peername,
+ path
+ );
+
+ rtems_pty_close_socket(&session->pty);
+
+ rtems_mutex_lock(&ctx->mtx);
+ LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
+ rtems_mutex_unlock(&ctx->mtx);
+ }
+}
+
+static void telnetd_sleep_after_error(void)
+{
+ /* If something went wrong, sleep for some time */
+ rtems_task_wake_after(10 * rtems_clock_get_ticks_per_second());
+}
+
+static void telnetd_server_task(rtems_task_argument arg)
+{
+ telnetd_context *ctx;
+
+ ctx = (telnetd_context *) arg;
+
+ while (true) {
+ telnetd_address peer;
+ socklen_t address_len;
+ int session_socket;
+ telnetd_session *session;
+
+ address_len = sizeof(peer.sin);
+ session_socket = accept(ctx->server_socket, &peer.sa, &address_len);
+ if (session_socket < 0) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot accept session");
+ telnetd_sleep_after_error();
+ continue;
+ };
+
+ rtems_mutex_lock(&ctx->mtx);
+ session = LIST_FIRST(&ctx->free_sessions);
+
+ if (session == NULL) {
+ rtems_mutex_unlock(&ctx->mtx);
+
+ (void)close(session_socket);
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: no free session available");
+ telnetd_sleep_after_error();
+ continue;
+ }
+
+ LIST_REMOVE(session, link);
+ rtems_mutex_unlock(&ctx->mtx);
+
+ rtems_pty_set_socket(&session->pty, session_socket);
+
+ if (
+ inet_ntop(
+ AF_INET,
+ &peer.sin.sin_addr,
+ session->peername,
+ sizeof(session->peername)
+ ) == NULL
+ ) {
+ strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
+ }
+
+ (void)rtems_event_system_send(session->task_id, RTEMS_EVENT_SYSTEM_SERVER);
+ }
+}
+
+static void telnetd_destroy_context(telnetd_context *ctx)
+{
+ telnetd_session *session;
+
+ LIST_FOREACH(session, &ctx->free_sessions, link) {
+ if (session->task_id != 0) {
+ (void)rtems_task_delete(session->task_id);
+ }
+
+ (void)unlink(rtems_pty_get_path(&session->pty));
+ }
+
+ if (ctx->server_socket >= 0) {
+ (void)close(ctx->server_socket);
+ }
+
+ rtems_mutex_destroy(&ctx->mtx);
+ free(ctx);
+}
+
+static rtems_status_code telnetd_create_server_socket(telnetd_context *ctx)
+{
+ telnetd_address srv;
+ socklen_t address_len;
+ int enable;
+
+ ctx->server_socket = socket(PF_INET, SOCK_STREAM, 0);
+ if (ctx->server_socket < 0) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server socket");
+ return RTEMS_UNSATISFIED;
+ }
+
+ enable = 1;
+ (void)setsockopt(
+ ctx->server_socket,
+ SOL_SOCKET,
+ SO_KEEPALIVE,
+ &enable,
+ sizeof(enable)
+ );
+
+ memset(&srv, 0, sizeof(srv));
+ srv.sin.sin_family = AF_INET;
+ srv.sin.sin_port = htons(ctx->config.port);
+ address_len = sizeof(srv.sin);
+
+ if (bind(ctx->server_socket, &srv.sa, address_len) != 0) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot bind server socket");
+ return RTEMS_RESOURCE_IN_USE;
+ };
+
+ if (listen(ctx->server_socket, ctx->config.client_maximum) != 0) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot listen on server socket");
+ return RTEMS_UNSATISFIED;
+ };
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_status_code telnetd_create_session_tasks(telnetd_context *ctx)
+{
+ uint16_t i;
+
+ ctx->task_id = rtems_task_self();
+
+ for (i = 0; i < ctx->config.client_maximum; ++i) {
+ telnetd_session *session;
+ rtems_status_code sc;
+ const char *path;
+ rtems_event_set events;
+
+ session = &ctx->sessions[i];
+ session->ctx = ctx;
+ rtems_mutex_init(&ctx->mtx, "Telnet");
+ LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
+
+ sc = rtems_task_create(
+ rtems_build_name('T', 'N', 'T', 'a' + i % 26),
+ ctx->config.priority,
+ ctx->config.stack_size,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_FLOATING_POINT,
+ &session->task_id
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session task");
+ return RTEMS_UNSATISFIED;
+ }
+
+ path = rtems_pty_initialize(&session->pty, i);
+ if (path == NULL) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session PTY");
+ return RTEMS_UNSATISFIED;
+ }
+
+ (void)rtems_task_start(
+ session->task_id,
+ telnetd_session_task,
+ (rtems_task_argument) session
+ );
+
+ (void)rtems_event_receive(
+ TELNETD_EVENT_SUCCESS | TELNETD_EVENT_ERROR,
+ RTEMS_WAIT | RTEMS_EVENT_ANY,
+ RTEMS_NO_TIMEOUT,
+ &events
+ );
+
+ if ((events & TELNETD_EVENT_ERROR) != 0) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot initialize session task");
+ return RTEMS_UNSATISFIED;
+ }
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
+{
+ telnetd_context *ctx;
+ rtems_status_code sc;
+ uint16_t client_maximum;
+
+ if (config->command == NULL) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: configuration with invalid command");
+ return RTEMS_INVALID_ADDRESS;
+ }
+
+ if (config->client_maximum == 0) {
+ client_maximum = 5;
+ } else {
+ client_maximum = config->client_maximum;
+ }
+
+ ctx = calloc(
+ 1,
+ sizeof(*ctx) + client_maximum * sizeof(ctx->sessions[0])
+ );
+ if (ctx == NULL) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot allocate server context");
+ return RTEMS_UNSATISFIED;
+ }
+
+ ctx->config = *config;
+ ctx->config.client_maximum = client_maximum;
+ ctx->server_socket = -1;
+ LIST_INIT(&ctx->free_sessions);
+
+ /* Check priority */
+#ifdef RTEMS_NETWORKING
+ if (ctx->config.priority == 0) {
+ ctx->config.priority = rtems_bsdnet_config.network_task_priority;
+ }
+#endif
+ if (ctx->config.priority == 0) {
+ ctx->config.priority = 100;
+ }
+
+ /* Check stack size */
+ if (ctx->config.stack_size == 0) {
+ ctx->config.stack_size = (size_t)32 * 1024;
+ }
+
+ if (ctx->config.port == 0) {
+ ctx->config.port = 23;
+ }
+
+ sc = telnetd_create_server_socket(ctx);
+ if (sc != RTEMS_SUCCESSFUL) {
+ telnetd_destroy_context(ctx);
+ return sc;
+ }
+
+ sc = telnetd_create_session_tasks(ctx);
+ if (sc != RTEMS_SUCCESSFUL) {
+ telnetd_destroy_context(ctx);
+ return sc;
+ }
+
+ sc = rtems_task_create(
+ rtems_build_name('T', 'N', 'T', 'D'),
+ ctx->config.priority,
+ RTEMS_MINIMUM_STACK_SIZE,
+ RTEMS_DEFAULT_MODES,
+ RTEMS_FLOATING_POINT,
+ &ctx->task_id
+ );
+ if (sc != RTEMS_SUCCESSFUL) {
+ syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server task");
+ telnetd_destroy_context(ctx);
+ return RTEMS_UNSATISFIED;
+ }
+
+ (void)rtems_task_start(
+ ctx->task_id,
+ telnetd_server_task,
+ (rtems_task_argument) ctx
+ );
+
+ syslog(
+ LOG_DAEMON | LOG_INFO,
+ "telnetd: started successfully on port %" PRIu16, ctx->config.port
+ );
+ return RTEMS_SUCCESSFUL;
+}