source: rtems/cpukit/telnetd/telnetd.c @ 629faf9

5
Last change on this file since 629faf9 was 629faf9, checked in by Sebastian Huber <sebastian.huber@…>, on 10/09/18 at 12:47:02

telnetd: Remove keep stdio feature

The Telnet service started via rtems_telnetd_start() had a keep stdio
feature. This just created a task and executed the command function in
a loop. For this kind of service we do not library support. This can
be done by an application task on its own. Remove this feature and
provide only the real Telnet server functionality.

Use syslog() for error and status messages.

Add test program for the Telnet server.

Close #3542.

  • Property mode set to 100644
File size: 10.9 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 embedded brains GmbH and others.
27 *
28 * embedded brains GmbH
29 * Obere Lagerstr. 30
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/socket.h>
44#include <netinet/in.h>
45#include <arpa/inet.h>
46#include <unistd.h>
47#include <stdlib.h>
48#include <stdio.h>
49#include <string.h>
50#include <syslog.h>
51
52#include <rtems.h>
53#include <rtems/error.h>
54#include <rtems/pty.h>
55#include <rtems/shell.h>
56#include <rtems/telnetd.h>
57#include <rtems/userenv.h>
58
59#ifdef RTEMS_NETWORKING
60#include <rtems/rtems_bsdnet.h>
61#endif
62
63#define PARANOIA
64
65typedef struct telnetd_context telnetd_context;
66
67struct shell_args {
68  rtems_pty_context  pty;
69  char               peername[16];
70  telnetd_context   *ctx;
71};
72
73struct telnetd_context {
74  rtems_telnetd_config_table config;
75  uint16_t                   active_clients;
76};
77
78typedef union uni_sa {
79  struct sockaddr_in sin;
80  struct sockaddr     sa;
81} uni_sa;
82
83static int sockpeername(int sock, char *buf, int bufsz);
84
85rtems_id telnetd_dflt_spawn(
86  const char *name,
87  unsigned priority,
88  unsigned stackSize,
89  void (*fn)(void*),
90  void *fnarg
91);
92
93/***********************************************************/
94static telnetd_context telnetd_instance;
95
96/*
97 * chrisj: this variable was global and with no declared interface in a header
98 *         file and with no means to set it so I have stopped it being global;
99 *         if this breaks any user they will have be to provide a formal
100 *         interface to get this change reverted.
101 */
102static const rtems_id (*telnetd_spawn_task)(
103  const char *,
104  unsigned,
105  unsigned,
106  void (*)(void*),
107  void *
108) = telnetd_dflt_spawn;
109
110static struct shell_args *grab_a_Connection(
111  telnetd_context *ctx,
112  int des_socket,
113  uni_sa *srv,
114  char *peername,
115  int sz
116)
117{
118  struct shell_args *args;
119  socklen_t size_adr = sizeof(srv->sin);
120  int acp_sock;
121
122  if (ctx->active_clients >= ctx->config.client_maximum) {
123    return NULL;
124  }
125
126  args = malloc(sizeof(*args));
127  if (args == NULL) {
128    perror("telnetd:malloc");
129    return NULL;
130  }
131
132  acp_sock = accept(des_socket,&srv->sa,&size_adr);
133  if (acp_sock<0) {
134    perror("telnetd:accept");
135    free(args);
136    return NULL;
137  };
138
139  if (telnet_get_pty(&args->pty, acp_sock) == NULL) {
140    syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY");
141    /* NOTE: failing 'do_get_pty()' closed the socket */
142    free(args);
143    return NULL;
144  }
145
146  if (sockpeername(acp_sock, peername, sz))
147    strncpy(peername, "<UNKNOWN>", sz);
148
149#ifdef PARANOIA
150  syslog(LOG_DAEMON | LOG_INFO,
151      "telnetd: accepted connection from %s on %s",
152      peername,
153      args->pty.name);
154#endif
155
156  ++ctx->active_clients;
157  args->ctx = ctx;
158  return args;
159}
160
161
162static void release_a_Connection(telnetd_context *ctx, const char *devname,
163  const char *peername, FILE **pstd, int n)
164{
165
166#ifdef PARANOIA
167  syslog( LOG_DAEMON | LOG_INFO,
168      "telnetd: releasing connection from %s on %s",
169      peername,
170      devname );
171#endif
172
173  --ctx->active_clients;
174
175  while (--n>=0)
176    if (pstd[n]) fclose(pstd[n]);
177
178  unlink(devname);
179}
180
181static int sockpeername(int sock, char *buf, int bufsz)
182{
183  uni_sa peer;
184  int    rval = sock < 0;
185  socklen_t len  = sizeof(peer.sin);
186
187  if ( !rval )
188    rval = getpeername(sock, &peer.sa, &len);
189
190  if ( !rval )
191    rval = !inet_ntop( AF_INET, &peer.sin.sin_addr, buf, bufsz );
192
193  return rval;
194}
195
196static void
197spawned_shell(void *arg);
198
199/***********************************************************/
200static void
201rtems_task_telnetd(void *task_argument)
202{
203  int                des_socket;
204  uni_sa             srv;
205  char               peername[16];
206  int                i=1;
207  int                size_adr;
208  struct shell_args *arg = NULL;
209  rtems_id           task_id;
210  telnetd_context   *ctx = task_argument;
211
212  if ((des_socket=socket(PF_INET,SOCK_STREAM,0))<0) {
213    perror("telnetd:socket");
214    rtems_task_exit();
215  };
216  (void)setsockopt(des_socket,SOL_SOCKET,SO_KEEPALIVE,&i,sizeof(i));
217
218  memset(&srv,0,sizeof(srv));
219  srv.sin.sin_family=AF_INET;
220  srv.sin.sin_port=htons(23);
221  size_adr=sizeof(srv.sin);
222  if ((bind(des_socket,&srv.sa,size_adr))<0) {
223    perror("telnetd:bind");
224    close(des_socket);
225    rtems_task_exit();
226  };
227  if ((listen(des_socket,5))<0) {
228    perror("telnetd:listen");
229          close(des_socket);
230    rtems_task_exit();
231  };
232
233  /* we don't redirect stdio as this probably
234   * was started from the console anyway ..
235   */
236  do {
237    arg = grab_a_Connection(ctx, des_socket, &srv, peername,
238      sizeof(peername));
239
240    if (arg == NULL) {
241      /* if something went wrong, sleep for some time */
242      sleep(10);
243      continue;
244    }
245
246    strncpy(arg->peername, peername, sizeof(arg->peername));
247
248    task_id = telnetd_spawn_task(
249      arg->pty.name,
250      ctx->config.priority,
251      ctx->config.stack_size,
252      spawned_shell,
253      arg
254    );
255    if (task_id == RTEMS_ID_NONE) {
256      FILE *dummy;
257
258      if ( telnetd_spawn_task != telnetd_dflt_spawn ) {
259        fprintf(stderr,"Telnetd: Unable to spawn child task\n");
260      }
261
262      /* hmm - the pty driver slot can only be
263       * released by opening and subsequently
264       * closing the PTY - this also closes
265       * the underlying socket. So we mock up
266       * a stream...
267       */
268
269      if ( !(dummy=fopen(arg->pty.name,"r+")) )
270        perror("Unable to dummy open the pty, losing a slot :-(");
271      release_a_Connection(ctx, arg->pty.name, peername, &dummy, 1);
272      free(arg);
273      sleep(2); /* don't accept connections too fast */
274    }
275  } while(1);
276
277  /* TODO: how to free the connection semaphore? But then -
278   *       stopping the daemon is probably only needed during
279   *       development/debugging.
280   *       Finalizer code should collect all the connection semaphore
281   *       counts and eventually clean up...
282   */
283  close(des_socket);
284}
285
286rtems_status_code rtems_telnetd_start(const rtems_telnetd_config_table* config)
287{
288  telnetd_context *ctx = &telnetd_instance;
289  rtems_id task_id;
290
291  if (config->command == NULL) {
292    syslog(LOG_DAEMON | LOG_ERR, "telnetd: configuration with invalid command");
293    return RTEMS_INVALID_ADDRESS;
294  }
295
296  if (ctx->config.command != NULL) {
297    syslog(LOG_DAEMON | LOG_ERR, "telnetd: already started");
298    return RTEMS_RESOURCE_IN_USE;
299  }
300
301  ctx->config = *config;
302
303  /* Check priority */
304#ifdef RTEMS_NETWORKING
305  if (ctx->config.priority == 0) {
306    ctx->config.priority = rtems_bsdnet_config.network_task_priority;
307  }
308#endif
309  if (ctx->config.priority == 0) {
310    ctx->config.priority = 100;
311  }
312
313  /* Check stack size */
314  if (ctx->config.stack_size == 0) {
315    ctx->config.stack_size = (size_t)32 * 1024;
316  }
317
318  if (ctx->config.client_maximum == 0) {
319    ctx->config.client_maximum = 5;
320  }
321
322  /* Spawn task */
323  task_id = telnetd_spawn_task(
324    "TNTD",
325    ctx->config.priority,
326    ctx->config.stack_size,
327    rtems_task_telnetd,
328    ctx
329  );
330  if (task_id == RTEMS_ID_NONE) {
331    ctx->config.command = NULL;
332    syslog(LOG_DAEMON | LOG_ERR, "telnetd: cannot create server task");
333    return RTEMS_UNSATISFIED;
334  }
335
336  syslog(LOG_DAEMON | LOG_INFO, "telnetd: started successfully");
337  return RTEMS_SUCCESSFUL;
338}
339
340/* utility wrapper */
341static void
342spawned_shell(void *targ)
343{
344  rtems_status_code    sc;
345  FILE                *nstd[3]={0};
346  FILE                *ostd[3]={ stdin, stdout, stderr };
347  int                  i=0;
348  struct shell_args  *arg = targ;
349  telnetd_context    *ctx = arg->ctx;
350  bool login_failed = false;
351  bool start = true;
352
353  sc=rtems_libio_set_private_env();
354
355  /* newlib hack/workaround. Before we change stdin/out/err we must make
356         * sure the internal data are initialized (fileno(stdout) has this sideeffect).
357   * This should probably be done from RTEMS' libc support layer...
358   * (T.S., newlibc-1.13; 2005/10)
359         */
360
361  fileno(stdout);
362
363  if (RTEMS_SUCCESSFUL != sc) {
364    rtems_error(sc,"rtems_libio_set_private_env");
365    goto cleanup;
366  }
367
368  /* redirect stdio */
369  for (i=0; i<3; i++) {
370    if ( !(nstd[i]=fopen(arg->pty.name,"r+")) ) {
371      perror("unable to open stdio");
372      goto cleanup;
373    }
374  }
375
376  stdin  = nstd[0];
377  stdout = nstd[1];
378  stderr = nstd[2];
379
380  /* call their routine */
381  if (ctx->config.login_check != NULL) {
382    start = rtems_shell_login_prompt(
383      stdin,
384      stderr,
385      arg->pty.name,
386      ctx->config.login_check
387    );
388    login_failed = !start;
389  }
390  if (start) {
391    ctx->config.command( arg->pty.name, ctx->config.arg);
392  }
393
394  stdin  = ostd[0];
395  stdout = ostd[1];
396  stderr = ostd[2];
397
398  if (login_failed) {
399    syslog(
400      LOG_AUTHPRIV | LOG_WARNING,
401      "telnetd: to many wrong passwords entered from %s",
402      arg->peername
403    );
404  }
405
406cleanup:
407  release_a_Connection(ctx, arg->pty.name, arg->peername, nstd, i);
408  free(arg);
409}
410
411struct wrap_delete_args {
412  void (*t)(void *);
413  void           *a;
414};
415
416static rtems_task
417wrap_delete(rtems_task_argument arg)
418{
419  struct wrap_delete_args     *pwa = (struct wrap_delete_args *)arg;
420  register void              (*t)(void *) = pwa->t;
421  register void               *a   = pwa->a;
422
423  /* free argument before calling function (which may never return if
424   * they choose to delete themselves)
425   */
426  free(pwa);
427  t(a);
428  rtems_task_exit();
429}
430
431rtems_id
432telnetd_dflt_spawn(const char *name, unsigned int priority, unsigned int stackSize, void (*fn)(void *), void* fnarg)
433{
434  rtems_status_code        sc;
435  rtems_id                 task_id = RTEMS_ID_NONE;
436  char                     nm[4] = {'X','X','X','X' };
437  struct wrap_delete_args *pwa = malloc(sizeof(*pwa));
438
439  strncpy(nm, name, 4);
440
441  if ( !pwa ) {
442    perror("Telnetd: no memory\n");
443    return RTEMS_ID_NONE;
444  }
445
446  pwa->t = fn;
447  pwa->a = fnarg;
448
449  if ((sc=rtems_task_create(
450    rtems_build_name(nm[0], nm[1], nm[2], nm[3]),
451      (rtems_task_priority)priority,
452      stackSize,
453      RTEMS_DEFAULT_MODES,
454      RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT,
455      &task_id)) ||
456    (sc=rtems_task_start(
457      task_id,
458      wrap_delete,
459      (rtems_task_argument)pwa))) {
460        free(pwa);
461        rtems_error(sc,"Telnetd: spawning task failed");
462        return RTEMS_ID_NONE;
463  }
464  return task_id;
465}
Note: See TracBrowser for help on using the repository browser.