source: rtems/cpukit/telnetd/telnetd.c @ 2aa8014

4.104.115
Last change on this file since 2aa8014 was cbd1e87, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 04/14/09 at 08:50:03

adapt copyright statements

  • Property mode set to 100644
File size: 12.1 KB
RevLine 
[bd520203]1/***********************************************************/
[b2712e3]2/*
[bd520203]3 *
4 *  The telnet DAEMON
5 *
6 *  Author: 17,may 2001
7 *
[8a775c27]8 *   WORK: fernando.ruiz@ctv.es
[bd520203]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)
[8a775c27]18 *
[bd520203]19 * Sorry but OOB is not still implemented. (This is the first version)
[b5bf8cd1]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.
[081583c]25 *
[cbd1e87]26 * Copyright (c) 2009 embedded brains GmbH and others.
27 *
[8a775c27]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.com/license/LICENSE.
37 *
38 * $Id$
[b2712e3]39 */
[b5bf8cd1]40
[bd520203]41#ifdef HAVE_CONFIG_H
42#include "config.h"
43#endif
[b2712e3]44
45#include <rtems.h>
46#include <rtems/error.h>
[bd520203]47#include <rtems/pty.h>
[b2712e3]48#include <rtems/shell.h>
49#include <rtems/telnetd.h>
[b5bf8cd1]50#include <rtems/bspIo.h>
[b2712e3]51#include <sys/socket.h>
52#include <netinet/in.h>
53#include <arpa/inet.h>
54#include <unistd.h>
55#include <stdlib.h>
56#include <stdio.h>
[b5bf8cd1]57#include <assert.h>
58#include <string.h>
59#include <syslog.h>
60
61#include <rtems/userenv.h>
62#include <rtems/error.h>
63#include <rtems/rtems_bsdnet.h>
64
65#define PARANOIA
66
67extern char *telnet_get_pty(int socket);
[c0cc985]68extern int   telnet_pty_initialize(void);
[b5bf8cd1]69
70struct shell_args {
[edcb982]71  char *devname;
72  void *arg;
73  char  peername[16];
74  char  delete_myself;
[b5bf8cd1]75};
76
77typedef union uni_sa {
[edcb982]78  struct sockaddr_in sin;
[8ad6681b]79  struct sockaddr     sa;
[b5bf8cd1]80} uni_sa;
81
82static int sockpeername(int sock, char *buf, int bufsz);
83
[8a775c27]84void *telnetd_dflt_spawn(
[edcb982]85  const char *name,
86  unsigned priority,
87  unsigned stackSize,
88  void (*fn)(void*),
89  void *fnarg
90);
[79061679]91
[b2712e3]92/***********************************************************/
[8a775c27]93static rtems_id telnetd_task_id = RTEMS_ID_NONE;
94
95void *(*telnetd_spawn_task)(
[1fae7b43]96  const char *,
97  unsigned,
98  unsigned,
99  void (*)(void*),
[8a775c27]100  void *
101) = telnetd_dflt_spawn;
[1fae7b43]102
103static char *grab_a_Connection(
104  int des_socket,
105  uni_sa *srv,
106  char *peername,
107  int sz
108)
[b5bf8cd1]109{
[edcb982]110  char *rval = 0;
[b5bf8cd1]111#if 0
[edcb982]112  socklen_t size_adr = sizeof(srv->sin);
[b5bf8cd1]113#else
[edcb982]114  /* 4.6 doesn't have socklen_t */
115  uint32_t size_adr = sizeof(srv->sin);
[b5bf8cd1]116#endif
[edcb982]117  int acp_sock;
[b5bf8cd1]118
[8ad6681b]119  acp_sock = accept(des_socket,&srv->sa,&size_adr);
[b5bf8cd1]120
[8ad6681b]121  if (acp_sock<0) {
122    perror("telnetd:accept");
123    goto bailout;
124  };
[b5bf8cd1]125
[8ad6681b]126  if ( !(rval=telnet_get_pty(acp_sock)) ) {
127    syslog( LOG_DAEMON | LOG_ERR, "telnetd: unable to obtain PTY");
128    /* NOTE: failing 'do_get_pty()' closed the socket */
129    goto bailout;
130  }
[b5bf8cd1]131
[8ad6681b]132  if (sockpeername(acp_sock, peername, sz))
133    strncpy(peername, "<UNKNOWN>", sz);
[b5bf8cd1]134
135#ifdef PARANOIA
[8ad6681b]136  syslog(LOG_DAEMON | LOG_INFO,
137      "telnetd: accepted connection from %s on %s",
138      peername,
139      rval);
[b5bf8cd1]140#endif
141
142bailout:
143
[8ad6681b]144  return rval;
[b5bf8cd1]145}
146
147
148static void release_a_Connection(char *devname, char *peername, FILE **pstd, int n)
149{
150
151#ifdef PARANOIA
[8ad6681b]152  syslog( LOG_DAEMON | LOG_INFO,
153      "telnetd: releasing connection from %s on %s",
154      peername,
155      devname );
[b5bf8cd1]156#endif
157
[8ad6681b]158  while (--n>=0)
159    if (pstd[n]) fclose(pstd[n]);
[b5bf8cd1]160
161}
162
163static int sockpeername(int sock, char *buf, int bufsz)
164{
[edcb982]165  uni_sa peer;
166  int    rval = sock < 0;
[b5bf8cd1]167#if 0
[edcb982]168  socklen_t len  = sizeof(peer.sin);
[b5bf8cd1]169#else
[edcb982]170  /* 4.6 doesn't have socklen_t */
171  uint32_t len  = sizeof(peer.sin);
[b5bf8cd1]172#endif
173
[edcb982]174  if ( !rval )
[8ad6681b]175    rval = getpeername(sock, &peer.sa, &len);
[b5bf8cd1]176
[8ad6681b]177  if ( !rval )
178    rval = !inet_ntop( AF_INET, &peer.sin.sin_addr, buf, bufsz );
[b5bf8cd1]179
[8ad6681b]180  return rval;
[b5bf8cd1]181}
182
183static void
184spawned_shell(void *arg);
185
[b2712e3]186/***********************************************************/
[b5bf8cd1]187static void
188rtems_task_telnetd(void *task_argument)
189{
[edcb982]190  int                des_socket;
191  uni_sa             srv;
192  char              *devname;
193  char               peername[16];
194  int                i=1;
195  int                size_adr;
[1fae7b43]196  struct shell_args *arg = NULL;
[b5bf8cd1]197
[8ad6681b]198  if ((des_socket=socket(PF_INET,SOCK_STREAM,0))<0) {
199    perror("telnetd:socket");
[8a775c27]200    telnetd_task_id = RTEMS_ID_NONE;
[8ad6681b]201    rtems_task_delete(RTEMS_SELF);
202  };
203  setsockopt(des_socket,SOL_SOCKET,SO_KEEPALIVE,&i,sizeof(i));
204
205  memset(&srv,0,sizeof(srv));
206  srv.sin.sin_family=AF_INET;
207  srv.sin.sin_port=htons(23);
208  size_adr=sizeof(srv.sin);
209  if ((bind(des_socket,&srv.sa,size_adr))<0) {
210    perror("telnetd:bind");
[1fae7b43]211    close(des_socket);
[8a775c27]212    telnetd_task_id = RTEMS_ID_NONE;
[8ad6681b]213    rtems_task_delete(RTEMS_SELF);
214  };
215  if ((listen(des_socket,5))<0) {
216    perror("telnetd:listen");
217          close(des_socket);
[8a775c27]218    telnetd_task_id = RTEMS_ID_NONE;
[8ad6681b]219    rtems_task_delete(RTEMS_SELF);
220  };
221
222  /* we don't redirect stdio as this probably
223   * was started from the console anyways..
224   */
225  do {
[8a775c27]226    if (rtems_telnetd_config.keep_stdio) {
227      bool start = true;
228      char device_name [32];
229
230      ttyname_r( 1, device_name, sizeof( device_name));
231      if (rtems_telnetd_config.login_check != NULL) {
232        start = rtems_shell_login_prompt(
233          stdin,
234          stderr,
235          device_name,
236          rtems_telnetd_config.login_check
237        );
238      }
239      if (start) {
240        rtems_telnetd_config.command( device_name, arg->arg);
241      } else {
242        syslog(
243          LOG_AUTHPRIV | LOG_WARNING,
244          "telnetd: to many wrong passwords entered from %s",
245          device_name
246        );
247      }
[8ad6681b]248    } else {
[1fae7b43]249      devname = grab_a_Connection(des_socket, &srv, peername, sizeof(peername));
250
251      if ( !devname ) {
[8a775c27]252        /* if something went wrong, sleep for some time */
253        sleep(10);
254        continue;
[1fae7b43]255      }
256
[edcb982]257      arg = malloc( sizeof(*arg) );
[8ad6681b]258
[edcb982]259      arg->devname = devname;
[8a775c27]260      arg->arg = rtems_telnetd_config.arg;
[edcb982]261      strncpy(arg->peername, peername, sizeof(arg->peername));
[8ad6681b]262
[8a775c27]263      telnetd_task_id = (rtems_id) telnetd_spawn_task(
264        devname,
265        rtems_telnetd_config.priority,
266        rtems_telnetd_config.stack_size,
267        spawned_shell,
268        arg
269      );
270      if (telnetd_task_id == RTEMS_ID_NONE) {
[edcb982]271        FILE *dummy;
[8ad6681b]272
[edcb982]273        if ( telnetd_spawn_task != telnetd_dflt_spawn ) {
274          fprintf(stderr,"Telnetd: Unable to spawn child task\n");
275        }
[8ad6681b]276
[edcb982]277        /* hmm - the pty driver slot can only be
278         * released by opening and subsequently
279         * closing the PTY - this also closes
280         * the underlying socket. So we mock up
281         * a stream...
282         */
[8ad6681b]283
[edcb982]284        if ( !(dummy=fopen(devname,"r+")) )
285          perror("Unable to dummy open the pty, losing a slot :-(");
286        release_a_Connection(devname, peername, &dummy, 1);
287        free(arg);
288        sleep(2); /* don't accept connections too fast */
289      }
[8ad6681b]290    }
291  } while(1);
[edcb982]292
[8a775c27]293  /* TODO: how to free the connection semaphore? But then -
[8ad6681b]294   *       stopping the daemon is probably only needed during
295   *       development/debugging.
296   *       Finalizer code should collect all the connection semaphore
297   *       counts and eventually clean up...
298   */
299  close(des_socket);
[8a775c27]300  telnetd_task_id = RTEMS_ID_NONE;
[b2712e3]301}
[b5bf8cd1]302
[8a775c27]303rtems_status_code rtems_telnetd_initialize( void)
[b5bf8cd1]304{
[8a775c27]305  rtems_status_code sc = RTEMS_SUCCESSFUL;
[8ad6681b]306
[8a775c27]307  if (telnetd_task_id != RTEMS_ID_NONE) {
308    fprintf(stderr, "telnetd already started\n");
309    return RTEMS_RESOURCE_IN_USE;
[8ad6681b]310  }
311
[8a775c27]312  if (rtems_telnetd_config.command == NULL) {
313    fprintf(stderr, "telnetd setup with invalid command\n");
314    return RTEMS_IO_ERROR;
315  }
[8ad6681b]316
317  if ( !telnet_pty_initialize() ) {
[8a775c27]318    fprintf(stderr, "telnetd cannot initialize PTY driver\n");
319    return RTEMS_IO_ERROR;
[8ad6681b]320  }
321
[8a775c27]322  /* Check priority */
323  if (rtems_telnetd_config.priority <= 0) {
324    rtems_telnetd_config.priority = rtems_bsdnet_config.network_task_priority;
325  }
326  if (rtems_telnetd_config.priority < 2) {
327    rtems_telnetd_config.priority = 100;
328  }
[edcb982]329
[8a775c27]330  /* Check stack size */
331  if (rtems_telnetd_config.stack_size <= 0) {
332    rtems_telnetd_config.stack_size = 32 * 1024;
[8ad6681b]333  }
[8a775c27]334
335  /* Spawn task */
336  telnetd_task_id = (rtems_id) telnetd_spawn_task(
337    "TNTD",
338    rtems_telnetd_config.priority,
339    RTEMS_MINIMUM_STACK_SIZE,
340    rtems_task_telnetd,
341    0
342  );
343  if (telnetd_task_id == RTEMS_ID_NONE) {
344    return RTEMS_IO_ERROR;
345  }
346
347  /* Print status */
348  if (!rtems_telnetd_config.keep_stdio) {
349    fprintf(
350      stderr,
351      "telnetd started with stacksize = %u and priority = %d\n",
352      (unsigned) rtems_telnetd_config.stack_size,
353      (unsigned) rtems_telnetd_config.priority
354    );
355  }
356
357  return RTEMS_SUCCESSFUL;
[b2712e3]358}
[b5bf8cd1]359
360/* utility wrapper */
361static void
362spawned_shell(void *targ)
363{
[edcb982]364  rtems_status_code    sc;
365  FILE                *nstd[3]={0};
366  FILE                *ostd[3]={ stdin, stdout, stderr };
367  int                  i=0;
368  struct shell_args  *arg = targ;
[8a775c27]369  bool login_failed = false;
370  bool start = true;
[b5bf8cd1]371
[8ad6681b]372  sc=rtems_libio_set_private_env();
[b5bf8cd1]373
[8ad6681b]374  /* newlib hack/workaround. Before we change stdin/out/err we must make
[b5bf8cd1]375         * sure the internal data are initialized (fileno(stdout) has this sideeffect).
[8ad6681b]376   * This should probably be done from RTEMS' libc support layer...
377   * (T.S., newlibc-1.13; 2005/10)
[b5bf8cd1]378         */
379
[8ad6681b]380  fileno(stdout);
[b5bf8cd1]381
[8ad6681b]382  if (RTEMS_SUCCESSFUL != sc) {
383    rtems_error(sc,"rtems_libio_set_private_env");
384    goto cleanup;
385  }
[b5bf8cd1]386
[8ad6681b]387  /* redirect stdio */
388  for (i=0; i<3; i++) {
389    if ( !(nstd[i]=fopen(arg->devname,"r+")) ) {
390      perror("unable to open stdio");
391      goto cleanup;
392    }
393  }
[b5bf8cd1]394
[8ad6681b]395  stdin  = nstd[0];
396  stdout = nstd[1];
397  stderr = nstd[2];
[b5bf8cd1]398
[edcb982]399  #if 0
400    printk("STDOUT is now %x (%x) (FD %i/%i)\n",
401           stdout,nstd[1],fileno(stdout),fileno(nstd[1]));
402    printf("hello\n");
403    write(fileno(stdout),"hellofd\n",8);
404  #endif
[b5bf8cd1]405
[8ad6681b]406  /* call their routine */
[8a775c27]407  if (rtems_telnetd_config.login_check != NULL) {
408    start = rtems_shell_login_prompt(
409      stdin,
410      stderr,
411      arg->devname,
412      rtems_telnetd_config.login_check
413    );
414    login_failed = !start;
415  }
416  if (start) {
417    rtems_telnetd_config.command( arg->devname, arg->arg);
418  }
[b5bf8cd1]419
[8ad6681b]420  stdin  = ostd[0];
421  stdout = ostd[1];
422  stderr = ostd[2];
[b5bf8cd1]423
[8a775c27]424  if (login_failed) {
425    syslog(
426      LOG_AUTHPRIV | LOG_WARNING,
427      "telnetd: to many wrong passwords entered from %s",
428      arg->peername
429    );
430  }
431
[b5bf8cd1]432cleanup:
[8ad6681b]433  release_a_Connection(arg->devname, arg->peername, nstd, i);
434  free(arg);
[b2712e3]435}
[b5bf8cd1]436
437struct wrap_delete_args {
[8ad6681b]438  void (*t)(void *);
439  void           *a;
[b5bf8cd1]440};
441
442static rtems_task
443wrap_delete(rtems_task_argument arg)
444{
[edcb982]445  struct wrap_delete_args     *pwa = (struct wrap_delete_args *)arg;
446  register void              (*t)(void *) = pwa->t;
447  register void               *a   = pwa->a;
[b5bf8cd1]448
[8ad6681b]449  /* free argument before calling function (which may never return if
450   * they choose to delete themselves)
451   */
452  free(pwa);
453  t(a);
454  rtems_task_delete(RTEMS_SELF);
[b5bf8cd1]455}
456
457void *
458telnetd_dflt_spawn(const char *name, unsigned int priority, unsigned int stackSize, void (*fn)(void *), void* fnarg)
459{
[edcb982]460  rtems_status_code        sc;
[8a775c27]461  rtems_id                 task_id = RTEMS_ID_NONE;
[edcb982]462  char                     nm[4] = {'X','X','X','X' };
463  struct wrap_delete_args *pwa = malloc(sizeof(*pwa));
[b5bf8cd1]464
[edcb982]465  strncpy(nm, name, 4);
[8ad6681b]466
[edcb982]467  if ( !pwa ) {
468    perror("Telnetd: no memory\n");
[8a775c27]469    return (void *) RTEMS_ID_NONE;
[edcb982]470  }
[8ad6681b]471
[edcb982]472  pwa->t = fn;
473  pwa->a = fnarg;
474
475  if ((sc=rtems_task_create(
476    rtems_build_name(nm[0], nm[1], nm[2], nm[3]),
477      (rtems_task_priority)priority,
478      stackSize,
479      RTEMS_DEFAULT_MODES,
480      RTEMS_DEFAULT_ATTRIBUTES | RTEMS_FLOATING_POINT,
481      &task_id)) ||
482    (sc=rtems_task_start(
483      task_id,
484      wrap_delete,
485      (rtems_task_argument)pwa))) {
486        free(pwa);
487        rtems_error(sc,"Telnetd: spawning task failed");
[8a775c27]488        return (void *) RTEMS_ID_NONE;
[edcb982]489  }
[8a775c27]490  return (void *) task_id;
[b5bf8cd1]491}
492
493/* convenience routines for CEXP (retrieve stdio descriptors
494 * from reent structure)
495 */
496#ifdef stdin
497static __inline__ FILE *
498_stdin(void)  { return stdin; }
499#undef stdin
500FILE *stdin(void)  { return _stdin(); }
501#endif
502#ifdef stdout
503static __inline__ FILE *
504_stdout(void) { return stdout; }
505#undef stdout
506FILE *stdout(void) { return _stdout(); }
507#endif
508#ifdef stderr
509static __inline__ FILE *
510_stderr(void) { return stderr; }
511#undef stderr
512FILE *stderr(void) { return _stderr(); }
513#endif
514
515/* MUST NOT USE stdin & friends below here !!!!!!!!!!!!! */
Note: See TracBrowser for help on using the repository browser.