source: rtems/cpukit/telnetd/telnetd.c @ 15d7261b

4.11
Last change on this file since 15d7261b was 15d7261b, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 18, 2014 at 6:09:09 AM

telnetd: Use socklen_t

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