Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:27

0001 /**
0002  * @file
0003  * 
0004  * @brief The telnet DAEMON
0005  *
0006  * After start the net you can start this daemon.
0007  * It uses the previously inited pseudo-terminales (pty.c)
0008  * getting a new terminal with getpty(). This function
0009  * gives a terminal name passing a opened socket like parameter.
0010  *
0011  * With register_telnetd() you add a new command in the shell to start
0012  * this daemon interactively. (Login in /dev/console of course)
0013  *
0014  * Sorry but OOB is not still implemented. (This is the first version)
0015  */
0016 
0017 /*
0018  * Copyright (c) 2001 Fernando Ruiz Casas <fruizcasas@gmail.com>
0019  *
0020  * Till Straumann <strauman@slac.stanford.edu>
0021  *  - made the 'shell' interface more generic, i.e. it is now
0022  *    possible to have 'telnetd' run an arbitrary 'shell'
0023  *    program.
0024  *
0025  * Copyright (C) 2009, 2018 embedded brains GmbH & Co. KG
0026  *
0027  * The license and distribution terms for this file may be
0028  * found in the file LICENSE in this distribution or at
0029  * http://www.rtems.org/license/LICENSE.
0030  */
0031 
0032 #ifdef HAVE_CONFIG_H
0033 #include "config.h"
0034 #endif
0035 
0036 #include <sys/queue.h>
0037 #include <sys/socket.h>
0038 #include <netinet/in.h>
0039 #include <arpa/inet.h>
0040 #include <inttypes.h>
0041 #include <unistd.h>
0042 #include <stdlib.h>
0043 #include <stdio.h>
0044 #include <string.h>
0045 #include <syslog.h>
0046 
0047 #include <rtems.h>
0048 #include <rtems/pty.h>
0049 #include <rtems/shell.h>
0050 #include <rtems/telnetd.h>
0051 #include <rtems/thread.h>
0052 #include <rtems/userenv.h>
0053 
0054 #define TELNETD_EVENT_SUCCESS RTEMS_EVENT_0
0055 
0056 #define TELNETD_EVENT_ERROR RTEMS_EVENT_1
0057 
0058 typedef struct telnetd_context telnetd_context;
0059 
0060 typedef struct telnetd_session {
0061   rtems_pty_context            pty;
0062   char                         peername[16];
0063   telnetd_context             *ctx;
0064   rtems_id                     task_id;
0065   LIST_ENTRY(telnetd_session)  link;
0066 } telnetd_session;
0067 
0068 struct telnetd_context {
0069   rtems_telnetd_config_table   config;
0070   int                          server_socket;
0071   rtems_id                     task_id;
0072   rtems_mutex                  mtx;
0073   LIST_HEAD(, telnetd_session) free_sessions;
0074   telnetd_session              sessions[RTEMS_ZERO_LENGTH_ARRAY];
0075 };
0076 
0077 typedef union {
0078   struct sockaddr_in sin;
0079   struct sockaddr    sa;
0080 } telnetd_address;
0081 
0082 RTEMS_NO_RETURN static void telnetd_session_fatal_error(
0083   const telnetd_context *ctx
0084 )
0085 {
0086   (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_ERROR);
0087   rtems_task_exit();
0088 }
0089 
0090 static bool telnetd_login(telnetd_context *ctx, telnetd_session *session)
0091 {
0092   bool success;
0093 
0094   if (ctx->config.login_check == NULL) {
0095     return true;
0096   }
0097 
0098   success = rtems_shell_login_prompt(
0099     stdin,
0100     stderr,
0101     session->pty.name,
0102     ctx->config.login_check
0103   );
0104 
0105   if (!success) {
0106     syslog(
0107       LOG_AUTHPRIV | LOG_WARNING,
0108       "telnetd: too many wrong passwords entered from %s",
0109       session->peername
0110     );
0111   }
0112 
0113   return success;
0114 }
0115 
0116 static void telnetd_session_task(rtems_task_argument arg)
0117 {
0118   rtems_status_code sc;
0119   telnetd_session *session;
0120   telnetd_context *ctx;
0121   const char *path;
0122 
0123   session = (telnetd_session *) arg;
0124   ctx = session->ctx;
0125 
0126   sc = rtems_libio_set_private_env();
0127   if (sc != RTEMS_SUCCESSFUL) {
0128     telnetd_session_fatal_error(ctx);
0129   }
0130 
0131   path = rtems_pty_get_path(&session->pty);
0132 
0133   stdin = fopen(path, "r+");
0134   if (stdin == NULL) {
0135     telnetd_session_fatal_error(ctx);
0136   }
0137 
0138   stdout = fopen(path, "r+");
0139   if (stdout == NULL) {
0140     telnetd_session_fatal_error(ctx);
0141   }
0142 
0143   stderr = fopen(path, "r+");
0144   if (stderr == NULL) {
0145     telnetd_session_fatal_error(ctx);
0146   }
0147 
0148   (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_SUCCESS);
0149 
0150   while (true) {
0151     rtems_event_set events;
0152 
0153     (void)rtems_event_system_receive(
0154       RTEMS_EVENT_SYSTEM_SERVER,
0155       RTEMS_WAIT | RTEMS_EVENT_ALL,
0156       RTEMS_NO_TIMEOUT,
0157       &events
0158     );
0159 
0160     syslog(
0161       LOG_DAEMON | LOG_INFO,
0162       "telnetd: accepted connection from %s on %s",
0163       session->peername,
0164       path
0165     );
0166 
0167     if (telnetd_login(ctx, session)) {
0168       (*ctx->config.command)(session->pty.name, ctx->config.arg);
0169     }
0170 
0171     syslog(
0172       LOG_DAEMON | LOG_INFO,
0173       "telnetd: releasing connection from %s on %s",
0174       session->peername,
0175       path
0176     );
0177 
0178     rtems_pty_close_socket(&session->pty);
0179 
0180     rtems_mutex_lock(&ctx->mtx);
0181     LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
0182     rtems_mutex_unlock(&ctx->mtx);
0183   }
0184 }
0185 
0186 static void telnetd_sleep_after_error(void)
0187 {
0188       /* If something went wrong, sleep for some time */
0189       rtems_task_wake_after(10 * rtems_clock_get_ticks_per_second());
0190 }
0191 
0192 static void telnetd_server_task(rtems_task_argument arg)
0193 {
0194   telnetd_context *ctx;
0195 
0196   ctx = (telnetd_context *) arg;
0197 
0198   while (true) {
0199     telnetd_address peer;
0200     socklen_t address_len;
0201     int session_socket;
0202     telnetd_session *session;
0203 
0204     address_len = sizeof(peer.sin);
0205     session_socket = accept(ctx->server_socket, &peer.sa, &address_len);
0206     if (session_socket < 0) {
0207       syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot accept session");
0208       telnetd_sleep_after_error();
0209       continue;
0210     };
0211 
0212     rtems_mutex_lock(&ctx->mtx);
0213     session = LIST_FIRST(&ctx->free_sessions);
0214 
0215     if (session == NULL) {
0216       rtems_mutex_unlock(&ctx->mtx);
0217 
0218       (void)close(session_socket);
0219       syslog(LOG_DAEMON | LOG_ERR, "telnetd: no free session available");
0220       telnetd_sleep_after_error();
0221       continue;
0222     }
0223 
0224     LIST_REMOVE(session, link);
0225     rtems_mutex_unlock(&ctx->mtx);
0226 
0227     rtems_pty_set_socket(&session->pty, session_socket);
0228 
0229     if (
0230       inet_ntop(
0231         AF_INET,
0232         &peer.sin.sin_addr,
0233         session->peername,
0234         sizeof(session->peername)
0235       ) == NULL
0236     ) {
0237       strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
0238     }
0239 
0240     (void)rtems_event_system_send(session->task_id, RTEMS_EVENT_SYSTEM_SERVER);
0241   }
0242 }
0243 
0244 static void telnetd_destroy_context(telnetd_context *ctx)
0245 {
0246   telnetd_session *session;
0247 
0248   LIST_FOREACH(session, &ctx->free_sessions, link) {
0249     if (session->task_id != 0) {
0250       (void)rtems_task_delete(session->task_id);
0251     }
0252 
0253     (void)unlink(rtems_pty_get_path(&session->pty));
0254   }
0255 
0256   if (ctx->server_socket >= 0) {
0257     (void)close(ctx->server_socket);
0258   }
0259 
0260   rtems_mutex_destroy(&ctx->mtx);
0261   free(ctx);
0262 }
0263 
0264 static rtems_status_code telnetd_create_server_socket(telnetd_context *ctx)
0265 {
0266   telnetd_address srv;
0267   socklen_t address_len;
0268   int enable;
0269 
0270   ctx->server_socket = socket(PF_INET, SOCK_STREAM, 0);
0271   if (ctx->server_socket < 0) {
0272     syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server socket");
0273     return RTEMS_UNSATISFIED;
0274   }
0275 
0276   enable = 1;
0277   (void)setsockopt(
0278     ctx->server_socket,
0279     SOL_SOCKET,
0280     SO_KEEPALIVE,
0281     &enable,
0282     sizeof(enable)
0283   );
0284 
0285   memset(&srv, 0, sizeof(srv));
0286   srv.sin.sin_family = AF_INET;
0287   srv.sin.sin_port = htons(ctx->config.port);
0288   address_len = sizeof(srv.sin);
0289 
0290   if (bind(ctx->server_socket, &srv.sa, address_len) != 0) {
0291     syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot bind server socket");
0292     return RTEMS_RESOURCE_IN_USE;
0293   };
0294 
0295   if (listen(ctx->server_socket, ctx->config.client_maximum) != 0) {
0296     syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot listen on server socket");
0297     return RTEMS_UNSATISFIED;
0298   };
0299 
0300   return RTEMS_SUCCESSFUL;
0301 }
0302 
0303 static rtems_status_code telnetd_create_session_tasks(telnetd_context *ctx)
0304 {
0305   uint16_t i;
0306 
0307   ctx->task_id = rtems_task_self();
0308 
0309   for (i = 0; i < ctx->config.client_maximum; ++i) {
0310     telnetd_session *session;
0311     rtems_status_code sc;
0312     const char *path;
0313     rtems_event_set events;
0314 
0315     session = &ctx->sessions[i];
0316     session->ctx = ctx;
0317     rtems_mutex_init(&ctx->mtx, "Telnet");
0318     LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
0319 
0320     sc = rtems_task_create(
0321       rtems_build_name('T', 'N', 'T', 'a' + i % 26),
0322       ctx->config.priority,
0323       ctx->config.stack_size,
0324       RTEMS_DEFAULT_MODES,
0325       RTEMS_FLOATING_POINT,
0326       &session->task_id
0327     );
0328     if (sc != RTEMS_SUCCESSFUL) {
0329       syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session task");
0330       return RTEMS_UNSATISFIED;
0331     }
0332 
0333     path = rtems_pty_initialize(&session->pty, i);
0334     if (path == NULL) {
0335       syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session PTY");
0336       return RTEMS_UNSATISFIED;
0337     }
0338 
0339     (void)rtems_task_start(
0340       session->task_id,
0341       telnetd_session_task,
0342       (rtems_task_argument) session
0343     );
0344 
0345     (void)rtems_event_receive(
0346       TELNETD_EVENT_SUCCESS | TELNETD_EVENT_ERROR,
0347       RTEMS_WAIT | RTEMS_EVENT_ANY,
0348       RTEMS_NO_TIMEOUT,
0349       &events
0350     );
0351 
0352     if ((events & TELNETD_EVENT_ERROR) != 0) {
0353       syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot initialize session task");
0354       return RTEMS_UNSATISFIED;
0355     }
0356   }
0357 
0358   return RTEMS_SUCCESSFUL;
0359 }
0360 
0361 rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
0362 {
0363   telnetd_context *ctx;
0364   rtems_status_code sc;
0365   uint16_t client_maximum;
0366 
0367   if (config->command == NULL) {
0368     syslog(LOG_DAEMON | LOG_ERR, "telnetd: configuration with invalid command");
0369     return RTEMS_INVALID_ADDRESS;
0370   }
0371 
0372   if (config->client_maximum == 0) {
0373     client_maximum = 5;
0374   } else {
0375     client_maximum = config->client_maximum;
0376   }
0377 
0378   ctx = calloc(
0379     1,
0380     sizeof(*ctx) + client_maximum * sizeof(ctx->sessions[0])
0381   );
0382   if (ctx == NULL) {
0383     syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot allocate server context");
0384     return RTEMS_UNSATISFIED;
0385   }
0386 
0387   ctx->config = *config;
0388   ctx->config.client_maximum = client_maximum;
0389   ctx->server_socket = -1;
0390   LIST_INIT(&ctx->free_sessions);
0391 
0392   /* Set priority */
0393   if (ctx->config.priority == 0) {
0394     ctx->config.priority = 100;
0395   }
0396 
0397   /* Check stack size */
0398   if (ctx->config.stack_size == 0) {
0399     ctx->config.stack_size = (size_t)32 * 1024;
0400   }
0401 
0402   if (ctx->config.port == 0) {
0403     ctx->config.port = 23;
0404   }
0405 
0406   sc = telnetd_create_server_socket(ctx);
0407   if (sc != RTEMS_SUCCESSFUL) {
0408     telnetd_destroy_context(ctx);
0409     return sc;
0410   }
0411 
0412   sc = telnetd_create_session_tasks(ctx);
0413   if (sc != RTEMS_SUCCESSFUL) {
0414     telnetd_destroy_context(ctx);
0415     return sc;
0416   }
0417 
0418   sc = rtems_task_create(
0419     rtems_build_name('T', 'N', 'T', 'D'),
0420     ctx->config.priority,
0421     RTEMS_MINIMUM_STACK_SIZE,
0422     RTEMS_DEFAULT_MODES,
0423     RTEMS_FLOATING_POINT,
0424     &ctx->task_id
0425   );
0426   if (sc != RTEMS_SUCCESSFUL) {
0427     syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server task");
0428     telnetd_destroy_context(ctx);
0429     return RTEMS_UNSATISFIED;
0430   }
0431 
0432   (void)rtems_task_start(
0433     ctx->task_id,
0434     telnetd_server_task,
0435     (rtems_task_argument) ctx
0436   );
0437 
0438   syslog(
0439     LOG_DAEMON | LOG_INFO,
0440     "telnetd: started successfully on port %" PRIu16, ctx->config.port
0441   );
0442   return RTEMS_SUCCESSFUL;
0443 }