source: rtems/cpukit/telnetd/telnetd.c

Last change on this file was 26b58b7e, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 10, 2018 at 11:12:50 AM

telnetd: Add server port to configuration

Close #3543.

  • Property mode set to 100644
File size: 10.7 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#ifdef RTEMS_NETWORKING
62#include <rtems/rtems_bsdnet.h>
63#endif
64
65#define TELNETD_EVENT_SUCCESS RTEMS_EVENT_0
66
67#define TELNETD_EVENT_ERROR RTEMS_EVENT_1
68
69typedef struct telnetd_context telnetd_context;
70
71typedef struct telnetd_session {
72  rtems_pty_context            pty;
73  char                         peername[16];
74  telnetd_context             *ctx;
75  rtems_id                     task_id;
76  LIST_ENTRY(telnetd_session)  link;
77} telnetd_session;
78
79struct telnetd_context {
80  rtems_telnetd_config_table   config;
81  int                          server_socket;
82  rtems_id                     task_id;
83  rtems_mutex                  mtx;
84  LIST_HEAD(, telnetd_session) free_sessions;
85  telnetd_session              sessions[RTEMS_ZERO_LENGTH_ARRAY];
86};
87
88typedef union {
89  struct sockaddr_in sin;
90  struct sockaddr    sa;
91} telnetd_address;
92
93RTEMS_NO_RETURN static void telnetd_session_fatal_error(
94  const telnetd_context *ctx
95)
96{
97  (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_ERROR);
98  rtems_task_exit();
99}
100
101static bool telnetd_login(telnetd_context *ctx, telnetd_session *session)
102{
103  bool success;
104
105  if (ctx->config.login_check == NULL) {
106    return true;
107  }
108
109  success = rtems_shell_login_prompt(
110    stdin,
111    stderr,
112    session->pty.name,
113    ctx->config.login_check
114  );
115
116  if (!success) {
117    syslog(
118      LOG_AUTHPRIV | LOG_WARNING,
119      "telnetd: too many wrong passwords entered from %s",
120      session->peername
121    );
122  }
123
124  return success;
125}
126
127static void telnetd_session_task(rtems_task_argument arg)
128{
129  rtems_status_code sc;
130  telnetd_session *session;
131  telnetd_context *ctx;
132  const char *path;
133
134  session = (telnetd_session *) arg;
135  ctx = session->ctx;
136
137  sc = rtems_libio_set_private_env();
138  if (sc != RTEMS_SUCCESSFUL) {
139    telnetd_session_fatal_error(ctx);
140  }
141
142  path = rtems_pty_get_path(&session->pty);
143
144  stdin = fopen(path, "r+");
145  if (stdin == NULL) {
146    telnetd_session_fatal_error(ctx);
147  }
148
149  stdout = fopen(path, "r+");
150  if (stdout == NULL) {
151    telnetd_session_fatal_error(ctx);
152  }
153
154  stderr = fopen(path, "r+");
155  if (stderr == NULL) {
156    telnetd_session_fatal_error(ctx);
157  }
158
159  (void)rtems_event_send(ctx->task_id, TELNETD_EVENT_SUCCESS);
160
161  while (true) {
162    rtems_event_set events;
163
164    (void)rtems_event_system_receive(
165      RTEMS_EVENT_SYSTEM_SERVER,
166      RTEMS_WAIT | RTEMS_EVENT_ALL,
167      RTEMS_NO_TIMEOUT,
168      &events
169    );
170
171    syslog(
172      LOG_DAEMON | LOG_INFO,
173      "telnetd: accepted connection from %s on %s",
174      session->peername,
175      path
176    );
177
178    if (telnetd_login(ctx, session)) {
179      (*ctx->config.command)(session->pty.name, ctx->config.arg);
180    }
181
182    syslog(
183      LOG_DAEMON | LOG_INFO,
184      "telnetd: releasing connection from %s on %s",
185      session->peername,
186      path
187    );
188
189    rtems_pty_close_socket(&session->pty);
190
191    rtems_mutex_lock(&ctx->mtx);
192    LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
193    rtems_mutex_unlock(&ctx->mtx);
194  }
195}
196
197static void telnetd_sleep_after_error(void)
198{
199      /* If something went wrong, sleep for some time */
200      rtems_task_wake_after(10 * rtems_clock_get_ticks_per_second());
201}
202
203static void telnetd_server_task(rtems_task_argument arg)
204{
205  telnetd_context *ctx;
206
207  ctx = (telnetd_context *) arg;
208
209  while (true) {
210    telnetd_address peer;
211    socklen_t address_len;
212    int session_socket;
213    telnetd_session *session;
214
215    address_len = sizeof(peer.sin);
216    session_socket = accept(ctx->server_socket, &peer.sa, &address_len);
217    if (session_socket < 0) {
218      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot accept session");
219      telnetd_sleep_after_error();
220      continue;
221    };
222
223    rtems_mutex_lock(&ctx->mtx);
224    session = LIST_FIRST(&ctx->free_sessions);
225
226    if (session == NULL) {
227      rtems_mutex_unlock(&ctx->mtx);
228
229      (void)close(session_socket);
230      syslog(LOG_DAEMON | LOG_ERR, "telnetd: no free session available");
231      telnetd_sleep_after_error();
232      continue;
233    }
234
235    LIST_REMOVE(session, link);
236    rtems_mutex_unlock(&ctx->mtx);
237
238    rtems_pty_set_socket(&session->pty, session_socket);
239
240    if (
241      inet_ntop(
242        AF_INET,
243        &peer.sin.sin_addr,
244        session->peername,
245        sizeof(session->peername)
246      ) == NULL
247    ) {
248      strlcpy(session->peername, "<UNKNOWN>", sizeof(session->peername));
249    }
250
251    (void)rtems_event_system_send(session->task_id, RTEMS_EVENT_SYSTEM_SERVER);
252  }
253}
254
255static void telnetd_destroy_context(telnetd_context *ctx)
256{
257  telnetd_session *session;
258
259  LIST_FOREACH(session, &ctx->free_sessions, link) {
260    if (session->task_id != 0) {
261      (void)rtems_task_delete(session->task_id);
262    }
263
264    (void)unlink(rtems_pty_get_path(&session->pty));
265  }
266
267  if (ctx->server_socket >= 0) {
268    (void)close(ctx->server_socket);
269  }
270
271  rtems_mutex_destroy(&ctx->mtx);
272  free(ctx);
273}
274
275static rtems_status_code telnetd_create_server_socket(telnetd_context *ctx)
276{
277  telnetd_address srv;
278  socklen_t address_len;
279  int enable;
280
281  ctx->server_socket = socket(PF_INET, SOCK_STREAM, 0);
282  if (ctx->server_socket < 0) {
283    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server socket");
284    return RTEMS_UNSATISFIED;
285  }
286
287  enable = 1;
288  (void)setsockopt(
289    ctx->server_socket,
290    SOL_SOCKET,
291    SO_KEEPALIVE,
292    &enable,
293    sizeof(enable)
294  );
295
296  memset(&srv, 0, sizeof(srv));
297  srv.sin.sin_family = AF_INET;
298  srv.sin.sin_port = htons(ctx->config.port);
299  address_len = sizeof(srv.sin);
300
301  if (bind(ctx->server_socket, &srv.sa, address_len) != 0) {
302    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot bind server socket");
303    return RTEMS_RESOURCE_IN_USE;
304  };
305
306  if (listen(ctx->server_socket, ctx->config.client_maximum) != 0) {
307    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot listen on server socket");
308    return RTEMS_UNSATISFIED;
309  };
310
311  return RTEMS_SUCCESSFUL;
312}
313
314static rtems_status_code telnetd_create_session_tasks(telnetd_context *ctx)
315{
316  uint16_t i;
317
318  ctx->task_id = rtems_task_self();
319
320  for (i = 0; i < ctx->config.client_maximum; ++i) {
321    telnetd_session *session;
322    rtems_status_code sc;
323    const char *path;
324    rtems_event_set events;
325
326    session = &ctx->sessions[i];
327    session->ctx = ctx;
328    rtems_mutex_init(&ctx->mtx, "Telnet");
329    LIST_INSERT_HEAD(&ctx->free_sessions, session, link);
330
331    sc = rtems_task_create(
332      rtems_build_name('T', 'N', 'T', 'a' + i % 26),
333      ctx->config.priority,
334      ctx->config.stack_size,
335      RTEMS_DEFAULT_MODES,
336      RTEMS_FLOATING_POINT,
337      &session->task_id
338    );
339    if (sc != RTEMS_SUCCESSFUL) {
340      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session task");
341      return RTEMS_UNSATISFIED;
342    }
343
344    path = rtems_pty_initialize(&session->pty, i);
345    if (path == NULL) {
346      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create session PTY");
347      return RTEMS_UNSATISFIED;
348    }
349
350    (void)rtems_task_start(
351      session->task_id,
352      telnetd_session_task,
353      (rtems_task_argument) session
354    );
355
356    (void)rtems_event_receive(
357      TELNETD_EVENT_SUCCESS | TELNETD_EVENT_ERROR,
358      RTEMS_WAIT | RTEMS_EVENT_ANY,
359      RTEMS_NO_TIMEOUT,
360      &events
361    );
362
363    if ((events & TELNETD_EVENT_ERROR) != 0) {
364      syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot initialize session task");
365      return RTEMS_UNSATISFIED;
366    }
367  }
368
369  return RTEMS_SUCCESSFUL;
370}
371
372rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
373{
374  telnetd_context *ctx;
375  rtems_status_code sc;
376  uint16_t client_maximum;
377
378  if (config->command == NULL) {
379    syslog(LOG_DAEMON | LOG_ERR, "telnetd: configuration with invalid command");
380    return RTEMS_INVALID_ADDRESS;
381  }
382
383  if (config->client_maximum == 0) {
384    client_maximum = 5;
385  } else {
386    client_maximum = config->client_maximum;
387  }
388
389  ctx = calloc(
390    1,
391    sizeof(*ctx) + client_maximum * sizeof(ctx->sessions[0])
392  );
393  if (ctx == NULL) {
394    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot allocate server context");
395    return RTEMS_UNSATISFIED;
396  }
397
398  ctx->config = *config;
399  ctx->config.client_maximum = client_maximum;
400  ctx->server_socket = -1;
401  LIST_INIT(&ctx->free_sessions);
402
403  /* Check priority */
404#ifdef RTEMS_NETWORKING
405  if (ctx->config.priority == 0) {
406    ctx->config.priority = rtems_bsdnet_config.network_task_priority;
407  }
408#endif
409  if (ctx->config.priority == 0) {
410    ctx->config.priority = 100;
411  }
412
413  /* Check stack size */
414  if (ctx->config.stack_size == 0) {
415    ctx->config.stack_size = (size_t)32 * 1024;
416  }
417
418  if (ctx->config.port == 0) {
419    ctx->config.port = 23;
420  }
421
422  sc = telnetd_create_server_socket(ctx);
423  if (sc != RTEMS_SUCCESSFUL) {
424    telnetd_destroy_context(ctx);
425    return sc;
426  }
427
428  sc = telnetd_create_session_tasks(ctx);
429  if (sc != RTEMS_SUCCESSFUL) {
430    telnetd_destroy_context(ctx);
431    return sc;
432  }
433
434  sc = rtems_task_create(
435    rtems_build_name('T', 'N', 'T', 'D'),
436    ctx->config.priority,
437    RTEMS_MINIMUM_STACK_SIZE,
438    RTEMS_DEFAULT_MODES,
439    RTEMS_FLOATING_POINT,
440    &ctx->task_id
441  );
442  if (sc != RTEMS_SUCCESSFUL) {
443    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server task");
444    telnetd_destroy_context(ctx);
445    return RTEMS_UNSATISFIED;
446  }
447
448  (void)rtems_task_start(
449    ctx->task_id,
450    telnetd_server_task,
451    (rtems_task_argument) ctx
452  );
453
454  syslog(
455    LOG_DAEMON | LOG_INFO,
456    "telnetd: started successfully on port %" PRIu16, ctx->config.port
457  );
458  return RTEMS_SUCCESSFUL;
459}
Note: See TracBrowser for help on using the repository browser.