1 /* vi: set sw=4 ts=4: */
4 * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
6 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8 * ---------------------------------------------------------------------------
9 * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
10 ****************************************************************************
12 * The telnetd manpage says it all:
14 * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
15 * a client, then creating a login process which has the slave side of the
16 * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
17 * master side of the pseudo-terminal, implementing the telnet protocol and
18 * passing characters between the remote client and the login process.
20 * Vladimir Oleynik <dzo@simtreas.ru> 2001
21 * Set process group corrections, initial busybox port
33 #include <arpa/telnet.h>
35 /* Structure that describes a session */
37 struct tsession *next;
39 int sockfd_read, sockfd_write, ptyfd;
41 /* two circular buffers */
42 /*char *buf1, *buf2;*/
43 /*#define TS_BUF1 ts->buf1*/
44 /*#define TS_BUF2 TS_BUF2*/
45 #define TS_BUF1 ((unsigned char*)(ts + 1))
46 #define TS_BUF2 (((unsigned char*)(ts + 1)) + BUFSIZE)
47 int rdidx1, wridx1, size1;
48 int rdidx2, wridx2, size2;
51 /* Two buffers are directly after tsession in malloced memory.
52 * Make whole thing fit in 4k */
53 enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
58 static struct tsession *sessions;
59 static const char *loginpath = "/bin/login";
60 static const char *issuefile = "/etc/issue.net";
64 Remove all IAC's from buf1 (received IACs are ignored and must be removed
65 so as to not be interpreted by the terminal). Make an uninterrupted
66 string of characters fit for the terminal. Do this by packing
67 all characters meant for the terminal sequentially towards the end of buf.
69 Return a pointer to the beginning of the characters meant for the terminal.
70 and make *num_totty the number of characters that should be sent to
73 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
74 past (bf + len) then that IAC will be left unprocessed and *processed
75 will be less than len.
77 CR-LF ->'s CR mapping is also done here, for convenience.
79 NB: may fail to remove iacs which wrap around buffer!
81 static unsigned char *
82 remove_iacs(struct tsession *ts, int *pnum_totty)
84 unsigned char *ptr0 = TS_BUF1 + ts->wridx1;
85 unsigned char *ptr = ptr0;
86 unsigned char *totty = ptr;
87 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
96 /* We map \r\n ==> \r for pragmatic reasons.
97 * Many client implementations send \r\n when
98 * the user hits the CarriageReturn key.
100 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
107 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
111 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
118 * TELOPT_NAWS support!
120 if ((ptr+2) >= end) {
121 /* only the beginning of the IAC is in the
122 buffer we were asked to process, we can't
123 process this char. */
127 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
129 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
132 break; /* incomplete, can't process */
133 ws.ws_col = (ptr[3] << 8) | ptr[4];
134 ws.ws_row = (ptr[5] << 8) | ptr[6];
135 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
139 /* skip 3-byte IAC non-SB cmd */
141 fprintf(stderr, "Ignoring IAC %s,%s\n",
142 TELCMD(ptr[1]), TELOPT(ptr[2]));
147 num_totty = totty - ptr0;
148 *pnum_totty = num_totty;
149 /* the difference between ptr and totty is number of iacs
150 we removed from the stream. Adjust buf1 accordingly. */
151 if ((ptr - totty) == 0) /* 99.999% of cases */
153 ts->wridx1 += ptr - totty;
154 ts->size1 -= ptr - totty;
155 /* move chars meant for the terminal towards the end of the buffer */
156 return memmove(ptr - num_totty, ptr0, num_totty);
160 * Converting single IAC into double on output
162 static size_t iac_safe_write(int fd, const char *buf, size_t count)
165 size_t wr, rc, total;
171 if (*buf == (char)IAC) {
172 static const char IACIAC[] ALIGN1 = { IAC, IAC };
173 rc = safe_write(fd, IACIAC, 2);
181 /* count != 0, *buf != IAC */
182 IACptr = memchr(buf, IAC, count);
186 rc = safe_write(fd, buf, wr);
193 /* here: rc - result of last short write */
194 if ((ssize_t)rc < 0) { /* error? */
202 static struct tsession *
204 USE_FEATURE_TELNETD_STANDALONE(int sock)
205 SKIP_FEATURE_TELNETD_STANDALONE(void)
207 const char *login_argv[2];
208 struct termios termbuf;
210 char tty_name[GETPTY_BUFSIZE];
211 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
213 /*ts->buf1 = (char *)(ts + 1);*/
214 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
216 /* Got a new connection, set up a tty. */
217 fd = xgetpty(tty_name);
222 #if ENABLE_FEATURE_TELNETD_STANDALONE
223 ts->sockfd_read = sock;
224 /* SO_KEEPALIVE by popular demand */
225 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
227 if (!sock) { /* We are called with fd 0 - we are in inetd mode */
228 sock++; /* so use fd 1 for output */
231 ts->sockfd_write = sock;
235 /* SO_KEEPALIVE by popular demand */
236 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
237 /* ts->sockfd_read = 0; - done by xzalloc */
238 ts->sockfd_write = 1;
242 /* Make the telnet client understand we will echo characters so it
243 * should not do it locally. We don't tell the client to run linemode,
244 * because we want to handle line editing and tab completion and other
245 * stuff that requires char-by-char support. */
247 static const char iacs_to_send[] ALIGN1 = {
248 IAC, DO, TELOPT_ECHO,
249 IAC, DO, TELOPT_NAWS,
250 /* This requires telnetd.ctrlSQ.patch (incomplete) */
251 /* IAC, DO, TELOPT_LFLOW, */
252 IAC, WILL, TELOPT_ECHO,
253 IAC, WILL, TELOPT_SGA
255 /* This confuses iac_safe_write(), it will try to duplicate
257 //memcpy(TS_BUF2, iacs_to_send, sizeof(iacs_to_send));
258 //ts->rdidx2 = sizeof(iacs_to_send);
259 //ts->size2 = sizeof(iacs_to_send);
260 /* So just stuff it into TCP stream! (no error check...) */
261 #if ENABLE_FEATURE_TELNETD_STANDALONE
262 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
264 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
266 /*ts->rdidx2 = 0; - xzalloc did it */
270 fflush(NULL); /* flush all streams */
271 pid = vfork(); /* NOMMU-friendly */
275 /* sock will be closed by caller */
276 bb_perror_msg("vfork");
286 /* Careful - we are after vfork! */
288 /* Restore default signal handling ASAP */
289 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
291 /* Make new session and process group */
294 /* Open the child's side of the tty. */
295 /* NB: setsid() disconnects from any previous ctty's. Therefore
296 * we must open child's side of the tty AFTER setsid! */
298 xopen(tty_name, O_RDWR); /* becomes our ctty */
301 tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
303 /* The pseudo-terminal allocated to the client is configured to operate in
304 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
305 tcgetattr(0, &termbuf);
306 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
307 termbuf.c_oflag |= ONLCR | XTABS;
308 termbuf.c_iflag |= ICRNL;
309 termbuf.c_iflag &= ~IXOFF;
310 /*termbuf.c_lflag &= ~ICANON;*/
311 tcsetattr_stdin_TCSANOW(&termbuf);
313 /* Uses FILE-based I/O to stdout, but does fflush(stdout),
314 * so should be safe with vfork.
315 * I fear, though, that some users will have ridiculously big
316 * issue files, and they may block writing to fd 1,
317 * (parent is supposed to read it, but parent waits
318 * for vforked child to exec!) */
319 print_login_issue(issuefile, tty_name);
321 /* Exec shell / login / whatever */
322 login_argv[0] = loginpath;
323 login_argv[1] = NULL;
324 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
325 * exec external program */
326 BB_EXECVP(loginpath, (char **)login_argv);
327 /* _exit is safer with vfork, and we shouldn't send message
328 * to remote clients anyway */
329 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", loginpath);*/
332 /* Must match getopt32 string */
334 OPT_WATCHCHILD = (1 << 2), /* -K */
335 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
336 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
337 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
340 #if ENABLE_FEATURE_TELNETD_STANDALONE
343 free_session(struct tsession *ts)
345 struct tsession *t = sessions;
347 if (option_mask32 & OPT_INETD)
350 /* Unlink this telnet session from the session list */
354 while (t->next != ts)
360 /* It was said that "normal" telnetd just closes ptyfd,
361 * doesn't send SIGKILL. When we close ptyfd,
362 * kernel sends SIGHUP to processes having slave side opened. */
363 kill(ts->shell_pid, SIGKILL);
364 waitpid(ts->shell_pid, NULL, 0);
367 close(ts->sockfd_read);
368 /* We do not need to close(ts->sockfd_write), it's the same
369 * as sockfd_read unless we are in inetd mode. But in inetd mode
370 * we do not reach this */
373 /* Scan all sessions and find new maxfd */
377 if (maxfd < ts->ptyfd)
379 if (maxfd < ts->sockfd_read)
380 maxfd = ts->sockfd_read;
382 /* Again, sockfd_write == sockfd_read here */
383 if (maxfd < ts->sockfd_write)
384 maxfd = ts->sockfd_write;
390 #else /* !FEATURE_TELNETD_STANDALONE */
392 /* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
393 #define free_session(ts) return 0
397 static void handle_sigchld(int sig UNUSED_PARAM)
402 /* Looping: more than one child may have exited */
404 pid = wait_any_nohang(NULL);
409 if (ts->shell_pid == pid) {
418 int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
419 int telnetd_main(int argc UNUSED_PARAM, char **argv)
421 fd_set rdfdset, wrfdset;
425 #if ENABLE_FEATURE_TELNETD_STANDALONE
426 #define IS_INETD (opt & OPT_INETD)
427 int master_fd = master_fd; /* be happy, gcc */
428 unsigned portnbr = 23;
429 char *opt_bindaddr = NULL;
438 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
439 * don't need to guess whether it's ok to pass -i to us */
440 opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
441 &issuefile, &loginpath
442 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
443 if (!IS_INETD /*&& !re_execed*/) {
444 /* inform that we start in standalone mode?
445 * May be useful when people forget to give -i */
446 /*bb_error_msg("listening for connections");*/
447 if (!(opt & OPT_FOREGROUND)) {
448 /* DAEMON_CHDIR_ROOT was giving inconsistent
449 * behavior with/without -F, -i */
450 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
453 /* Redirect log to syslog early, if needed */
454 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
455 openlog(applet_name, LOG_PID, LOG_DAEMON);
456 logmode = LOGMODE_SYSLOG;
458 USE_FEATURE_TELNETD_STANDALONE(
460 portnbr = xatou16(opt_portnbr);
463 /* Used to check access(loginpath, X_OK) here. Pointless.
464 * exec will do this for us for free later. */
466 #if ENABLE_FEATURE_TELNETD_STANDALONE
468 sessions = make_new_session(0);
469 if (!sessions) /* pty opening or vfork problem, exit */
470 return 1; /* make_new_session prints error message */
472 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
473 xlisten(master_fd, 1);
476 sessions = make_new_session();
477 if (!sessions) /* pty opening or vfork problem, exit */
478 return 1; /* make_new_session prints error message */
481 /* We don't want to die if just one session is broken */
482 signal(SIGPIPE, SIG_IGN);
484 if (opt & OPT_WATCHCHILD)
485 signal(SIGCHLD, handle_sigchld);
486 else /* prevent dead children from becoming zombies */
487 signal(SIGCHLD, SIG_IGN);
490 This is how the buffers are used. The arrows indicate the movement
492 +-------+ wridx1++ +------+ rdidx1++ +----------+
493 | | <-------------- | buf1 | <-------------- | |
494 | | size1-- +------+ size1++ | |
496 | | rdidx2++ +------+ wridx2++ | |
497 | | --------------> | buf2 | --------------> | |
498 +-------+ size2++ +------+ size2-- +----------+
500 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
501 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
503 Each session has got two buffers. Buffers are circular. If sizeN == 0,
504 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
511 /* Select on the master socket, all telnet sockets and their
512 * ptys if there is room in their session buffers.
513 * NB: scalability problem: we recalculate entire bitmap
514 * before each select. Can be a problem with 500+ connections. */
517 struct tsession *next = ts->next; /* in case we free ts. */
518 if (ts->shell_pid == -1) {
519 /* Child died and we detected that */
522 if (ts->size1 > 0) /* can write to pty */
523 FD_SET(ts->ptyfd, &wrfdset);
524 if (ts->size1 < BUFSIZE) /* can read from socket */
525 FD_SET(ts->sockfd_read, &rdfdset);
526 if (ts->size2 > 0) /* can write to socket */
527 FD_SET(ts->sockfd_write, &wrfdset);
528 if (ts->size2 < BUFSIZE) /* can read from pty */
529 FD_SET(ts->ptyfd, &rdfdset);
534 FD_SET(master_fd, &rdfdset);
535 /* This is needed because free_session() does not
536 * take master_fd into account when it finds new
537 * maxfd among remaining fd's */
538 if (master_fd > maxfd)
542 count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
544 goto again; /* EINTR or ENOMEM */
546 #if ENABLE_FEATURE_TELNETD_STANDALONE
547 /* First check for and accept new sessions. */
548 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
550 struct tsession *new_ts;
552 fd = accept(master_fd, NULL, NULL);
555 /* Create a new session and link it into our active list */
556 new_ts = make_new_session(fd);
558 new_ts->next = sessions;
566 /* Then check for data tunneling. */
568 while (ts) { /* For all sessions... */
569 struct tsession *next = ts->next; /* in case we free ts. */
571 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
574 /* Write to pty from buffer 1. */
575 ptr = remove_iacs(ts, &num_totty);
576 count = safe_write(ts->ptyfd, ptr, num_totty);
584 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
588 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
589 /* Write to socket from buffer 2. */
590 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
591 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2 + ts->wridx2), count);
599 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
603 /* Should not be needed, but... remove_iacs is actually buggy
604 * (it cannot process iacs which wrap around buffer's end)!
605 * Since properly fixing it requires writing bigger code,
606 * we rely instead on this code making it virtually impossible
607 * to have wrapped iac (people don't type at 2k/second).
608 * It also allows for bigger reads in common case. */
609 if (ts->size1 == 0) {
613 if (ts->size2 == 0) {
618 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
619 /* Read from socket to buffer 1. */
620 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
621 count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count);
623 if (count < 0 && errno == EAGAIN)
627 /* Ignore trailing NUL if it is there */
628 if (!TS_BUF1[ts->rdidx1 + count - 1]) {
633 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
637 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
638 /* Read from pty to buffer 2. */
639 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
640 count = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, count);
642 if (count < 0 && errno == EAGAIN)
648 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */