File indexing completed on 2025-05-11 08:24:27
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
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
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
0393 if (ctx->config.priority == 0) {
0394 ctx->config.priority = 100;
0395 }
0396
0397
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 }