Tomato 1.28
[tomato.git] / release / src / router / busybox / networking / telnetd.c
blob4c5ea3ab39a8319aa424ea3b3a236d2e0714ebd8
1 /* vi: set sw=4 ts=4: */
2 /*
3 * Simple telnet server
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
24 #define DEBUG 0
26 #include "libbb.h"
27 #include <syslog.h>
29 #if DEBUG
30 #define TELCMDS
31 #define TELOPTS
32 #endif
33 #include <arpa/telnet.h>
35 /* Structure that describes a session */
36 struct tsession {
37 struct tsession *next;
38 pid_t shell_pid;
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 };
56 /* Globals */
57 static int maxfd;
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
71 the terminal.
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);
88 int num_totty;
90 while (ptr < end) {
91 if (*ptr != IAC) {
92 char c = *ptr;
94 *totty++ = c;
95 ptr++;
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'))
101 ptr++;
102 continue;
105 if ((ptr+1) >= end)
106 break;
107 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
108 ptr += 2;
109 continue;
111 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
112 *totty++ = ptr[1];
113 ptr += 2;
114 continue;
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. */
124 break;
127 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
129 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
130 struct winsize ws;
131 if ((ptr+8) >= end)
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);
136 ptr += 9;
137 continue;
139 /* skip 3-byte IAC non-SB cmd */
140 #if DEBUG
141 fprintf(stderr, "Ignoring IAC %s,%s\n",
142 TELCMD(ptr[1]), TELOPT(ptr[2]));
143 #endif
144 ptr += 3;
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 */
152 return ptr0;
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)
164 const char *IACptr;
165 size_t wr, rc, total;
167 total = 0;
168 while (1) {
169 if (count == 0)
170 return total;
171 if (*buf == (char)IAC) {
172 static const char IACIAC[] ALIGN1 = { IAC, IAC };
173 rc = safe_write(fd, IACIAC, 2);
174 if (rc != 2)
175 break;
176 buf++;
177 total++;
178 count--;
179 continue;
181 /* count != 0, *buf != IAC */
182 IACptr = memchr(buf, IAC, count);
183 wr = count;
184 if (IACptr)
185 wr = IACptr - buf;
186 rc = safe_write(fd, buf, wr);
187 if (rc != wr)
188 break;
189 buf += rc;
190 total += rc;
191 count -= rc;
193 /* here: rc - result of last short write */
194 if ((ssize_t)rc < 0) { /* error? */
195 if (total == 0)
196 return rc;
197 rc = 0;
199 return total + rc;
202 /* Must match getopt32 string */
203 enum {
204 OPT_WATCHCHILD = (1 << 2), /* -K */
205 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
206 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
207 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
210 static struct tsession *
211 make_new_session(
212 USE_FEATURE_TELNETD_STANDALONE(int master_fd, int sock)
213 SKIP_FEATURE_TELNETD_STANDALONE(void)
215 const char *login_argv[2];
216 struct termios termbuf;
217 int fd, pid;
218 char tty_name[GETPTY_BUFSIZE];
219 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
221 /*ts->buf1 = (char *)(ts + 1);*/
222 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
224 /* Got a new connection, set up a tty. */
225 fd = xgetpty(tty_name);
226 if (fd > maxfd)
227 maxfd = fd;
228 ts->ptyfd = fd;
229 ndelay_on(fd);
230 #if ENABLE_FEATURE_TELNETD_STANDALONE
231 ts->sockfd_read = sock;
232 /* SO_KEEPALIVE by popular demand */
233 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
234 ndelay_on(sock);
235 if (!sock) { /* We are called with fd 0 - we are in inetd mode */
236 sock++; /* so use fd 1 for output */
237 ndelay_on(sock);
239 ts->sockfd_write = sock;
240 if (sock > maxfd)
241 maxfd = sock;
242 #else
243 /* SO_KEEPALIVE by popular demand */
244 setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
245 /* ts->sockfd_read = 0; - done by xzalloc */
246 ts->sockfd_write = 1;
247 ndelay_on(0);
248 ndelay_on(1);
249 #endif
250 /* Make the telnet client understand we will echo characters so it
251 * should not do it locally. We don't tell the client to run linemode,
252 * because we want to handle line editing and tab completion and other
253 * stuff that requires char-by-char support. */
255 static const char iacs_to_send[] ALIGN1 = {
256 IAC, DO, TELOPT_ECHO,
257 IAC, DO, TELOPT_NAWS,
258 /* This requires telnetd.ctrlSQ.patch (incomplete) */
259 /* IAC, DO, TELOPT_LFLOW, */
260 IAC, WILL, TELOPT_ECHO,
261 IAC, WILL, TELOPT_SGA
263 /* This confuses iac_safe_write(), it will try to duplicate
264 * each IAC... */
265 //memcpy(TS_BUF2, iacs_to_send, sizeof(iacs_to_send));
266 //ts->rdidx2 = sizeof(iacs_to_send);
267 //ts->size2 = sizeof(iacs_to_send);
268 /* So just stuff it into TCP stream! (no error check...) */
269 #if ENABLE_FEATURE_TELNETD_STANDALONE
270 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
271 #else
272 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
273 #endif
274 /*ts->rdidx2 = 0; - xzalloc did it */
275 /*ts->size2 = 0;*/
278 fflush(NULL); /* flush all streams */
279 pid = vfork(); /* NOMMU-friendly */
280 if (pid < 0) {
281 free(ts);
282 close(fd);
283 /* sock will be closed by caller */
284 bb_perror_msg("vfork");
285 return NULL;
287 if (pid > 0) {
288 /* Parent */
289 ts->shell_pid = pid;
290 return ts;
293 /* Child */
294 /* Careful - we are after vfork! */
296 /* Restore default signal handling ASAP */
297 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
299 #if ENABLE_FEATURE_TELNETD_STANDALONE
300 if (!(option_mask32 & OPT_INETD)) {
301 struct tsession *tp = sessions;
302 while (tp) {
303 close(tp->ptyfd);
304 close(tp->sockfd_read);
305 /* sockfd_write == sockfd_read for standalone telnetd */
306 /*close(tp->sockfd_write);*/
307 tp = tp->next;
310 #endif
312 /* Make new session and process group */
313 setsid();
315 close(fd);
316 #if ENABLE_FEATURE_TELNETD_STANDALONE
317 close(sock);
318 if (master_fd >= 0)
319 close(master_fd);
320 #endif
322 /* Open the child's side of the tty. */
323 /* NB: setsid() disconnects from any previous ctty's. Therefore
324 * we must open child's side of the tty AFTER setsid! */
325 close(0);
326 xopen(tty_name, O_RDWR); /* becomes our ctty */
327 xdup2(0, 1);
328 xdup2(0, 2);
329 tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
331 /* The pseudo-terminal allocated to the client is configured to operate in
332 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
333 tcgetattr(0, &termbuf);
334 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
335 termbuf.c_oflag |= ONLCR | XTABS;
336 termbuf.c_iflag |= ICRNL;
337 termbuf.c_iflag &= ~IXOFF;
338 /*termbuf.c_lflag &= ~ICANON;*/
339 tcsetattr_stdin_TCSANOW(&termbuf);
341 /* Uses FILE-based I/O to stdout, but does fflush(stdout),
342 * so should be safe with vfork.
343 * I fear, though, that some users will have ridiculously big
344 * issue files, and they may block writing to fd 1,
345 * (parent is supposed to read it, but parent waits
346 * for vforked child to exec!) */
347 print_login_issue(issuefile, tty_name);
349 /* Exec shell / login / whatever */
350 login_argv[0] = loginpath;
351 login_argv[1] = NULL;
352 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
353 * exec external program */
354 BB_EXECVP(loginpath, (char **)login_argv);
355 /* _exit is safer with vfork, and we shouldn't send message
356 * to remote clients anyway */
357 _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", loginpath);*/
360 #if ENABLE_FEATURE_TELNETD_STANDALONE
362 static void
363 free_session(struct tsession *ts)
365 struct tsession *t = sessions;
367 if (option_mask32 & OPT_INETD)
368 exit(EXIT_SUCCESS);
370 /* Unlink this telnet session from the session list */
371 if (t == ts)
372 sessions = ts->next;
373 else {
374 while (t->next != ts)
375 t = t->next;
376 t->next = ts->next;
379 #if 0
380 /* It was said that "normal" telnetd just closes ptyfd,
381 * doesn't send SIGKILL. When we close ptyfd,
382 * kernel sends SIGHUP to processes having slave side opened. */
383 kill(ts->shell_pid, SIGKILL);
384 waitpid(ts->shell_pid, NULL, 0);
385 #endif
386 close(ts->ptyfd);
387 close(ts->sockfd_read);
388 /* We do not need to close(ts->sockfd_write), it's the same
389 * as sockfd_read unless we are in inetd mode. But in inetd mode
390 * we do not reach this */
391 free(ts);
393 /* Scan all sessions and find new maxfd */
394 maxfd = 0;
395 ts = sessions;
396 while (ts) {
397 if (maxfd < ts->ptyfd)
398 maxfd = ts->ptyfd;
399 if (maxfd < ts->sockfd_read)
400 maxfd = ts->sockfd_read;
401 #if 0
402 /* Again, sockfd_write == sockfd_read here */
403 if (maxfd < ts->sockfd_write)
404 maxfd = ts->sockfd_write;
405 #endif
406 ts = ts->next;
410 #else /* !FEATURE_TELNETD_STANDALONE */
412 /* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
413 #define free_session(ts) return 0
415 #endif
417 static void handle_sigchld(int sig UNUSED_PARAM)
419 pid_t pid;
420 struct tsession *ts;
422 /* Looping: more than one child may have exited */
423 while (1) {
424 pid = wait_any_nohang(NULL);
425 if (pid <= 0)
426 break;
427 ts = sessions;
428 while (ts) {
429 if (ts->shell_pid == pid) {
430 ts->shell_pid = -1;
431 break;
433 ts = ts->next;
438 int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
439 int telnetd_main(int argc UNUSED_PARAM, char **argv)
441 fd_set rdfdset, wrfdset;
442 unsigned opt;
443 int count;
444 struct tsession *ts;
445 #if ENABLE_FEATURE_TELNETD_STANDALONE
446 #define IS_INETD (opt & OPT_INETD)
447 int master_fd = master_fd; /* be happy, gcc */
448 unsigned portnbr = 23;
449 char *opt_bindaddr = NULL;
450 char *opt_portnbr;
451 #else
452 enum {
453 IS_INETD = 1,
454 master_fd = -1,
455 portnbr = 23,
457 #endif
458 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
459 * don't need to guess whether it's ok to pass -i to us */
460 opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
461 &issuefile, &loginpath
462 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
463 if (!IS_INETD /*&& !re_execed*/) {
464 /* inform that we start in standalone mode?
465 * May be useful when people forget to give -i */
466 /*bb_error_msg("listening for connections");*/
467 if (!(opt & OPT_FOREGROUND)) {
468 /* DAEMON_CHDIR_ROOT was giving inconsistent
469 * behavior with/without -F, -i */
470 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
473 /* Redirect log to syslog early, if needed */
474 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
475 openlog(applet_name, LOG_PID, LOG_DAEMON);
476 logmode = LOGMODE_SYSLOG;
478 USE_FEATURE_TELNETD_STANDALONE(
479 if (opt & OPT_PORT)
480 portnbr = xatou16(opt_portnbr);
483 /* Used to check access(loginpath, X_OK) here. Pointless.
484 * exec will do this for us for free later. */
486 #if ENABLE_FEATURE_TELNETD_STANDALONE
487 if (IS_INETD) {
488 sessions = make_new_session(-1, 0);
489 if (!sessions) /* pty opening or vfork problem, exit */
490 return 1; /* make_new_session prints error message */
491 } else {
492 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
493 xlisten(master_fd, 1);
495 #else
496 sessions = make_new_session();
497 if (!sessions) /* pty opening or vfork problem, exit */
498 return 1; /* make_new_session prints error message */
499 #endif
501 /* We don't want to die if just one session is broken */
502 signal(SIGPIPE, SIG_IGN);
504 if (opt & OPT_WATCHCHILD)
505 signal(SIGCHLD, handle_sigchld);
506 else /* prevent dead children from becoming zombies */
507 signal(SIGCHLD, SIG_IGN);
510 This is how the buffers are used. The arrows indicate the movement
511 of data.
512 +-------+ wridx1++ +------+ rdidx1++ +----------+
513 | | <-------------- | buf1 | <-------------- | |
514 | | size1-- +------+ size1++ | |
515 | pty | | socket |
516 | | rdidx2++ +------+ wridx2++ | |
517 | | --------------> | buf2 | --------------> | |
518 +-------+ size2++ +------+ size2-- +----------+
520 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
521 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
523 Each session has got two buffers. Buffers are circular. If sizeN == 0,
524 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
525 rdidxN == wridxN.
527 again:
528 FD_ZERO(&rdfdset);
529 FD_ZERO(&wrfdset);
531 /* Select on the master socket, all telnet sockets and their
532 * ptys if there is room in their session buffers.
533 * NB: scalability problem: we recalculate entire bitmap
534 * before each select. Can be a problem with 500+ connections. */
535 ts = sessions;
536 while (ts) {
537 struct tsession *next = ts->next; /* in case we free ts. */
538 if (ts->shell_pid == -1) {
539 /* Child died and we detected that */
540 free_session(ts);
541 } else {
542 if (ts->size1 > 0) /* can write to pty */
543 FD_SET(ts->ptyfd, &wrfdset);
544 if (ts->size1 < BUFSIZE) /* can read from socket */
545 FD_SET(ts->sockfd_read, &rdfdset);
546 if (ts->size2 > 0) /* can write to socket */
547 FD_SET(ts->sockfd_write, &wrfdset);
548 if (ts->size2 < BUFSIZE) /* can read from pty */
549 FD_SET(ts->ptyfd, &rdfdset);
551 ts = next;
553 if (!IS_INETD) {
554 FD_SET(master_fd, &rdfdset);
555 /* This is needed because free_session() does not
556 * take master_fd into account when it finds new
557 * maxfd among remaining fd's */
558 if (master_fd > maxfd)
559 maxfd = master_fd;
562 count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
563 if (count < 0)
564 goto again; /* EINTR or ENOMEM */
566 #if ENABLE_FEATURE_TELNETD_STANDALONE
567 /* First check for and accept new sessions. */
568 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
569 int fd;
570 struct tsession *new_ts;
572 fd = accept(master_fd, NULL, NULL);
573 if (fd < 0)
574 goto again;
575 /* Create a new session and link it into our active list */
576 new_ts = make_new_session(master_fd, fd);
577 if (new_ts) {
578 new_ts->next = sessions;
579 sessions = new_ts;
580 } else {
581 close(fd);
584 #endif
586 /* Then check for data tunneling. */
587 ts = sessions;
588 while (ts) { /* For all sessions... */
589 struct tsession *next = ts->next; /* in case we free ts. */
591 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
592 int num_totty;
593 unsigned char *ptr;
594 /* Write to pty from buffer 1. */
595 ptr = remove_iacs(ts, &num_totty);
596 count = safe_write(ts->ptyfd, ptr, num_totty);
597 if (count < 0) {
598 if (errno == EAGAIN)
599 goto skip1;
600 goto kill_session;
602 ts->size1 -= count;
603 ts->wridx1 += count;
604 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
605 ts->wridx1 = 0;
607 skip1:
608 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
609 /* Write to socket from buffer 2. */
610 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
611 count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2 + ts->wridx2), count);
612 if (count < 0) {
613 if (errno == EAGAIN)
614 goto skip2;
615 goto kill_session;
617 ts->size2 -= count;
618 ts->wridx2 += count;
619 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
620 ts->wridx2 = 0;
622 skip2:
623 /* Should not be needed, but... remove_iacs is actually buggy
624 * (it cannot process iacs which wrap around buffer's end)!
625 * Since properly fixing it requires writing bigger code,
626 * we rely instead on this code making it virtually impossible
627 * to have wrapped iac (people don't type at 2k/second).
628 * It also allows for bigger reads in common case. */
629 if (ts->size1 == 0) {
630 ts->rdidx1 = 0;
631 ts->wridx1 = 0;
633 if (ts->size2 == 0) {
634 ts->rdidx2 = 0;
635 ts->wridx2 = 0;
638 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
639 /* Read from socket to buffer 1. */
640 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
641 count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count);
642 if (count <= 0) {
643 if (count < 0 && errno == EAGAIN)
644 goto skip3;
645 goto kill_session;
647 /* Ignore trailing NUL if it is there */
648 if (!TS_BUF1[ts->rdidx1 + count - 1]) {
649 --count;
651 ts->size1 += count;
652 ts->rdidx1 += count;
653 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
654 ts->rdidx1 = 0;
656 skip3:
657 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
658 /* Read from pty to buffer 2. */
659 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
660 count = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, count);
661 if (count <= 0) {
662 if (count < 0 && errno == EAGAIN)
663 goto skip4;
664 goto kill_session;
666 ts->size2 += count;
667 ts->rdidx2 += count;
668 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
669 ts->rdidx2 = 0;
671 skip4:
672 ts = next;
673 continue;
674 kill_session:
675 free_session(ts);
676 ts = next;
679 goto again;