source: rtems/cpukit/telnetd/telnetd.c @ b597c0d

4.115
Last change on this file since b597c0d was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

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