source: rtems/cpukit/telnetd/telnetd.c

Last change on this file was 71521ff2, checked in by Vijay Kumar Banerjee <vijay@…>, on Apr 13, 2021 at 6:45:56 PM

telnetd.c: Remove RTEMS_NETWORKING check

Set the priority manually to make telnetd compatible with the

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