source: rtems/cpukit/telnetd/telnetd.c

Last change on this file was bcef89f2, checked in by Sebastian Huber <sebastian.huber@…>, on 05/19/23 at 06:18:25

Update company name

The embedded brains GmbH & Co. KG is the legal successor of embedded
brains GmbH.

  • Property mode set to 100644
File size: 10.3 KB
RevLine 
[5f8c41c]1/**
2 * @file
3 *
4 * @brief The telnet DAEMON
[650ac700]5 *
6 * After start the net you can start this daemon.
7 * It uses the previously inited pseudo-terminales (pty.c)
8 * getting a new terminal with getpty(). This function
9 * gives a terminal name passing a opened socket like parameter.
10 *
11 * With register_telnetd() you add a new command in the shell to start
12 * this daemon interactively. (Login in /dev/console of course)
13 *
14 * Sorry but OOB is not still implemented. (This is the first version)
[5f8c41c]15 */
16
17/*
18 * Copyright (c) 2001 Fernando Ruiz Casas <fruizcasas@gmail.com>
[650ac700]19 *
20 * Till Straumann <strauman@slac.stanford.edu>
21 *  - made the 'shell' interface more generic, i.e. it is now
22 *    possible to have 'telnetd' run an arbitrary 'shell'
23 *    program.
24 *
[bcef89f2]25 * Copyright (C) 2009, 2018 embedded brains GmbH & Co. KG
[650ac700]26 *
27 * The license and distribution terms for this file may be
28 * found in the file LICENSE in this distribution or at
29 * http://www.rtems.org/license/LICENSE.
30 */
31
32#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
36#include <sys/queue.h>
37#include <sys/socket.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40#include <inttypes.h>
41#include <unistd.h>
42#include <stdlib.h>
43#include <stdio.h>
44#include <string.h>
45#include <syslog.h>
46
47#include <rtems.h>
48#include <rtems/pty.h>
49#include <rtems/shell.h>
50#include <rtems/telnetd.h>
51#include <rtems/thread.h>
52#include <rtems/userenv.h>
53
54#define TELNETD_EVENT_SUCCESS RTEMS_EVENT_0
55
56#define TELNETD_EVENT_ERROR RTEMS_EVENT_1
57
58typedef struct telnetd_context telnetd_context;
59
60typedef struct telnetd_session {
61  rtems_pty_context            pty;
62  char                         peername[16];
63  telnetd_context             *ctx;
64  rtems_id                     task_id;
65  LIST_ENTRY(telnetd_session)  link;
66} telnetd_session;
67
68struct telnetd_context {
69  rtems_telnetd_config_table   config;
70  int                          server_socket;
71  rtems_id                     task_id;
72  rtems_mutex                  mtx;
73  LIST_HEAD(, telnetd_session) free_sessions;
74  telnetd_session              sessions[RTEMS_ZERO_LENGTH_ARRAY];
75};
76
77typedef union {
78  struct sockaddr_in sin;
79  struct sockaddr    sa;
80} telnetd_address;
81
82RTEMS_NO_RETURN static void telnetd_session_fatal_error(
83  const telnetd_context *ctx
84)
85{
86  (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_ERROR);
87  rtems_task_exit();
88}
89
90static bool telnetd_login(telnetd_context *ctx, telnetd_session *session)
91{
92  bool success;
93
94  if (ctx->config.login_check == NULL) {
95    return true;
96  }
97
98  success = rtems_shell_login_prompt(
99    stdin,
100    stderr,
101    session->pty.name,
102    ctx->config.login_check
103  );
104
105  if (!success) {
106    syslog(
107      LOG_AUTHPRIV | LOG_WARNING,
108      "telnetd: too many wrong passwords entered from %s",
109      session->peername
110    );
111  }
112
113  return success;
114}
115
116static void telnetd_session_task(rtems_task_argument arg)
117{
118  rtems_status_code sc;
119  telnetd_session *session;
120  telnetd_context *ctx;
121  const char *path;
122
123  session = (telnetd_session *) arg;
124  ctx = session->ctx;
125
126  sc = rtems_libio_set_private_env();
127  if (sc != RTEMS_SUCCESSFUL) {
128    telnetd_session_fatal_error(ctx);
129  }
130
131  path = rtems_pty_get_path(&session->pty);
132
133  stdin = fopen(path, "r+");
134  if (stdin == NULL) {
135    telnetd_session_fatal_error(ctx);
136  }
137
138  stdout = fopen(path, "r+");
139  if (stdout == NULL) {
140    telnetd_session_fatal_error(ctx);
141  }
142
143  stderr = fopen(path, "r+");
144  if (stderr == NULL) {
145    telnetd_session_fatal_error(ctx);
146  }
147
148  (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_SUCCESS);
149
150  while (true) {
151    rtems_event_set events;
152
153    (void)rtems_event_system_receive(
154      RTEMS_EVENT_SYSTEM_SERVER,
155      RTEMS_WAIT | RTEMS_EVENT_ALL,
156      RTEMS_NO_TIMEOUT,
157      &events
158    );
159
160    syslog(
161      LOG_DAEMON | LOG_INFO,
162      "telnetd: accepted connection from %s on %s",
163      session->peername,
164      path
165    );
166
167    if (telnetd_login(ctx, session)) {
168      (*ctx->config.command)(session->pty.name, ctx->config.arg);
169    }
170
171    syslog(
172      LOG_DAEMON | LOG_INFO,
173      "telnetd: releasing connection from %s on %s",
174      session->peername,
175      path
176    );
177
178    rtems_pty_close_socket(&session->pty);
179
180    rtems_mutex_lock(&ctx->mtx);
181    LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
182    rtems_mutex_unlock(&ctx->mtx);
183  }
184}
185
186static void telnetd_sleep_after_error(void)
187{
188      /* If something went wrong, sleep for some time */
189      rtems_task_wake_after(10 * rtems_clock_get_ticks_per_second());
190}
191
192static void telnetd_server_task(rtems_task_argument arg)
193{
194  telnetd_context *ctx;
195
196  ctx = (telnetd_context *) arg;
197
198  while (true) {
199    telnetd_address peer;
200    socklen_t address_len;
201    int session_socket;
202    telnetd_session *session;
203
204    address_len = sizeof(peer.sin);
205    session_socket = accept(ctx->server_socket, &peer.sa, &address_len);
206    if (session_socket < 0) {
207      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot accept session");
208      telnetd_sleep_after_error();
209      continue;
210    };
211
212    rtems_mutex_lock(&ctx->mtx);
213    session = LIST_FIRST(&ctx->free_sessions);
214
215    if (session == NULL) {
216      rtems_mutex_unlock(&ctx->mtx);
217
218      (void)close(session_socket);
219      syslog(LOG_DAEMON | LOG_ERR, "telnetd: no free session available");
220      telnetd_sleep_after_error();
221      continue;
222    }
223
224    LIST_REMOVE(session, link);
225    rtems_mutex_unlock(&ctx->mtx);
226
227    rtems_pty_set_socket(&session->pty, session_socket);
228
229    if (
230      inet_ntop(
231        AF_INET,
232        &peer.sin.sin_addr,
233        session->peername,
234        sizeof(session->peername)
235      ) == NULL
236    ) {
237      strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
238    }
239
240    (void)rtems_event_system_send(session->task_id, RTEMS_EVENT_SYSTEM_SERVER);
241  }
242}
243
244static void telnetd_destroy_context(telnetd_context *ctx)
245{
246  telnetd_session *session;
247
248  LIST_FOREACH(session, &ctx->free_sessions, link) {
249    if (session->task_id != 0) {
250      (void)rtems_task_delete(session->task_id);
251    }
252
253    (void)unlink(rtems_pty_get_path(&session->pty));
254  }
255
256  if (ctx->server_socket >= 0) {
257    (void)close(ctx->server_socket);
258  }
259
260  rtems_mutex_destroy(&ctx->mtx);
261  free(ctx);
262}
263
264static rtems_status_code telnetd_create_server_socket(telnetd_context *ctx)
265{
266  telnetd_address srv;
267  socklen_t address_len;
268  int enable;
269
270  ctx->server_socket = socket(PF_INET, SOCK_STREAM, 0);
271  if (ctx->server_socket < 0) {
272    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server socket");
273    return RTEMS_UNSATISFIED;
274  }
275
276  enable = 1;
277  (void)setsockopt(
278    ctx->server_socket,
279    SOL_SOCKET,
280    SO_KEEPALIVE,
281    &enable,
282    sizeof(enable)
283  );
284
285  memset(&srv, 0, sizeof(srv));
286  srv.sin.sin_family = AF_INET;
287  srv.sin.sin_port = htons(ctx->config.port);
288  address_len = sizeof(srv.sin);
289
290  if (bind(ctx->server_socket, &srv.sa, address_len) != 0) {
291    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot bind server socket");
292    return RTEMS_RESOURCE_IN_USE;
293  };
294
295  if (listen(ctx->server_socket, ctx->config.client_maximum) != 0) {
296    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot listen on server socket");
297    return RTEMS_UNSATISFIED;
298  };
299
300  return RTEMS_SUCCESSFUL;
301}
302
303static rtems_status_code telnetd_create_session_tasks(telnetd_context *ctx)
304{
305  uint16_t i;
306
307  ctx->task_id = rtems_task_self();
308
309  for (i = 0; i < ctx->config.client_maximum; ++i) {
310    telnetd_session *session;
311    rtems_status_code sc;
312    const char *path;
313    rtems_event_set events;
314
315    session = &ctx->sessions[i];
316    session->ctx = ctx;
317    rtems_mutex_init(&ctx->mtx, "Telnet");
318    LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
319
320    sc = rtems_task_create(
321      rtems_build_name('T', 'N', 'T', 'a' + i % 26),
322      ctx->config.priority,
323      ctx->config.stack_size,
324      RTEMS_DEFAULT_MODES,
325      RTEMS_FLOATING_POINT,
326      &session->task_id
327    );
328    if (sc != RTEMS_SUCCESSFUL) {
329      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session task");
330      return RTEMS_UNSATISFIED;
331    }
332
333    path = rtems_pty_initialize(&session->pty, i);
334    if (path == NULL) {
335      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session PTY");
336      return RTEMS_UNSATISFIED;
337    }
338
339    (void)rtems_task_start(
340      session->task_id,
341      telnetd_session_task,
342      (rtems_task_argument) session
343    );
344
345    (void)rtems_event_receive(
346      TELNETD_EVENT_SUCCESS | TELNETD_EVENT_ERROR,
347      RTEMS_WAIT | RTEMS_EVENT_ANY,
348      RTEMS_NO_TIMEOUT,
349      &events
350    );
351
352    if ((events & TELNETD_EVENT_ERROR) != 0) {
353      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot initialize session task");
354      return RTEMS_UNSATISFIED;
355    }
356  }
357
358  return RTEMS_SUCCESSFUL;
359}
360
361rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
362{
363  telnetd_context *ctx;
364  rtems_status_code sc;
365  uint16_t client_maximum;
366
367  if (config->command == NULL) {
368    syslog(LOG_DAEMON | LOG_ERR, "telnetd: configuration with invalid command");
369    return RTEMS_INVALID_ADDRESS;
370  }
371
372  if (config->client_maximum == 0) {
373    client_maximum = 5;
374  } else {
375    client_maximum = config->client_maximum;
376  }
377
378  ctx = calloc(
379    1,
380    sizeof(*ctx) + client_maximum * sizeof(ctx->sessions[0])
381  );
382  if (ctx == NULL) {
383    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot allocate server context");
384    return RTEMS_UNSATISFIED;
385  }
386
387  ctx->config = *config;
388  ctx->config.client_maximum = client_maximum;
389  ctx->server_socket = -1;
390  LIST_INIT(&ctx->free_sessions);
391
[71521ff2]392  /* Set priority */
[650ac700]393  if (ctx->config.priority == 0) {
394    ctx->config.priority = 100;
395  }
396
397  /* Check stack size */
398  if (ctx->config.stack_size == 0) {
399    ctx->config.stack_size = (size_t)32 * 1024;
400  }
401
402  if (ctx->config.port == 0) {
403    ctx->config.port = 23;
404  }
405
406  sc = telnetd_create_server_socket(ctx);
407  if (sc != RTEMS_SUCCESSFUL) {
408    telnetd_destroy_context(ctx);
409    return sc;
410  }
411
412  sc = telnetd_create_session_tasks(ctx);
413  if (sc != RTEMS_SUCCESSFUL) {
414    telnetd_destroy_context(ctx);
415    return sc;
416  }
417
418  sc = rtems_task_create(
419    rtems_build_name('T', 'N', 'T', 'D'),
420    ctx->config.priority,
421    RTEMS_MINIMUM_STACK_SIZE,
422    RTEMS_DEFAULT_MODES,
423    RTEMS_FLOATING_POINT,
424    &ctx->task_id
425  );
426  if (sc != RTEMS_SUCCESSFUL) {
427    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server task");
428    telnetd_destroy_context(ctx);
429    return RTEMS_UNSATISFIED;
430  }
431
432  (void)rtems_task_start(
433    ctx->task_id,
434    telnetd_server_task,
435    (rtems_task_argument) ctx
436  );
437
438  syslog(
439    LOG_DAEMON | LOG_INFO,
440    "telnetd: started successfully on port %" PRIu16, ctx->config.port
441  );
442  return RTEMS_SUCCESSFUL;
443}
Note: See TracBrowser for help on using the repository browser.