source: rtems-libbsd/rtemsbsd/telnetd/telnetd.c @ 65c65bb

5-freebsd-12freebsd-9.3
Last change on this file since 65c65bb was 65c65bb, checked in by Chris Johns <chrisj@…>, on Jul 1, 2016 at 5:49:52 AM

Add telnetd as service to rc.conf.

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