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;
38 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 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
78 what is the escape character? We aren't handling that situation here.
80 CR-LF ->'s CR mapping is also done here, for convenience.
82 NB: may fail to remove iacs which wrap around buffer!
84 static unsigned char *
85 remove_iacs(struct tsession *ts, int *pnum_totty)
87 unsigned char *ptr0 = TS_BUF1 + ts->wridx1;
88 unsigned char *ptr = ptr0;
89 unsigned char *totty = ptr;
90 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
99 /* We now map \r\n ==> \r for pragmatic reasons.
100 * Many client implementations send \r\n when
101 * the user hits the CarriageReturn key.
103 if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
107 * TELOPT_NAWS support!
109 if ((ptr+2) >= end) {
110 /* only the beginning of the IAC is in the
111 buffer we were asked to process, we can't
112 process this char. */
117 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
119 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
123 break; /* incomplete, can't process */
124 ws.ws_col = (ptr[3] << 8) | ptr[4];
125 ws.ws_row = (ptr[5] << 8) | ptr[6];
126 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
129 /* skip 3-byte IAC non-SB cmd */
131 fprintf(stderr, "Ignoring IAC %s,%s\n",
132 TELCMD(ptr[1]), TELOPT(ptr[2]));
139 num_totty = totty - ptr0;
140 *pnum_totty = num_totty;
141 /* the difference between ptr and totty is number of iacs
142 we removed from the stream. Adjust buf1 accordingly. */
143 if ((ptr - totty) == 0) /* 99.999% of cases */
145 ts->wridx1 += ptr - totty;
146 ts->size1 -= ptr - totty;
147 /* move chars meant for the terminal towards the end of the buffer */
148 return memmove(ptr - num_totty, ptr0, num_totty);
152 static struct tsession *
154 USE_FEATURE_TELNETD_STANDALONE(int sock)
155 SKIP_FEATURE_TELNETD_STANDALONE(void)
157 const char *login_argv[2];
158 struct termios termbuf;
160 char tty_name[GETPTY_BUFSIZE];
161 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
163 /*ts->buf1 = (char *)(ts + 1);*/
164 /*ts->buf2 = ts->buf1 + BUFSIZE;*/
166 /* Got a new connection, set up a tty. */
167 fd = getpty(tty_name);
169 bb_error_msg("can't create pty");
176 #if ENABLE_FEATURE_TELNETD_STANDALONE
177 ts->sockfd_read = sock;
179 if (!sock) { /* We are called with fd 0 - we are in inetd mode */
180 sock++; /* so use fd 1 for output */
183 ts->sockfd_write = sock;
187 /* ts->sockfd_read = 0; - done by xzalloc */
188 ts->sockfd_write = 1;
192 /* Make the telnet client understand we will echo characters so it
193 * should not do it locally. We don't tell the client to run linemode,
194 * because we want to handle line editing and tab completion and other
195 * stuff that requires char-by-char support. */
197 static const char iacs_to_send[] ALIGN1 = {
198 IAC, DO, TELOPT_ECHO,
199 IAC, DO, TELOPT_NAWS,
200 IAC, DO, TELOPT_LFLOW,
201 IAC, WILL, TELOPT_ECHO,
202 IAC, WILL, TELOPT_SGA
204 memcpy(TS_BUF2, iacs_to_send, sizeof(iacs_to_send));
205 ts->rdidx2 = sizeof(iacs_to_send);
206 ts->size2 = sizeof(iacs_to_send);
209 fflush(NULL); /* flush all streams */
210 pid = vfork(); /* NOMMU-friendly */
214 /* sock will be closed by caller */
215 bb_perror_msg("vfork");
225 /* Careful - we are after vfork! */
227 /* make new session and process group */
230 /* Restore default signal handling */
231 bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
233 /* open the child's side of the tty. */
234 /* NB: setsid() disconnects from any previous ctty's. Therefore
235 * we must open child's side of the tty AFTER setsid! */
236 fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
240 while (fd > 2) close(fd--);
241 tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
243 /* The pseudo-terminal allocated to the client is configured to operate in
244 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
245 tcgetattr(0, &termbuf);
246 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
247 termbuf.c_oflag |= ONLCR | XTABS;
248 termbuf.c_iflag |= ICRNL;
249 termbuf.c_iflag &= ~IXOFF;
250 /*termbuf.c_lflag &= ~ICANON;*/
251 tcsetattr(0, TCSANOW, &termbuf);
253 /* Uses FILE-based I/O to stdout, but does fflush(stdout),
254 * so should be safe with vfork.
255 * I fear, though, that some users will have ridiculously big
256 * issue files, and they may block writing to fd 1,
257 * (parent is supposed to read it, but parent waits
258 * for vforked child to exec!) */
259 print_login_issue(issuefile, NULL);
261 /* Exec shell / login / whatever */
262 login_argv[0] = loginpath;
263 login_argv[1] = NULL;
264 /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
265 * exec external program */
266 BB_EXECVP(loginpath, (char **)login_argv);
267 /* _exit is safer with vfork, and we shouldn't send message
268 * to remote clients anyway */
269 _exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/
272 /* Must match getopt32 string */
274 OPT_WATCHCHILD = (1 << 2), /* -K */
275 OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
276 OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */
277 OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
280 #if ENABLE_FEATURE_TELNETD_STANDALONE
283 free_session(struct tsession *ts)
285 struct tsession *t = sessions;
287 if (option_mask32 & OPT_INETD)
290 /* Unlink this telnet session from the session list */
294 while (t->next != ts)
300 /* It was said that "normal" telnetd just closes ptyfd,
301 * doesn't send SIGKILL. When we close ptyfd,
302 * kernel sends SIGHUP to processes having slave side opened. */
303 kill(ts->shell_pid, SIGKILL);
304 wait4(ts->shell_pid, NULL, 0, NULL);
307 close(ts->sockfd_read);
308 /* We do not need to close(ts->sockfd_write), it's the same
309 * as sockfd_read unless we are in inetd mode. But in inetd mode
310 * we do not reach this */
313 /* Scan all sessions and find new maxfd */
317 if (maxfd < ts->ptyfd)
319 if (maxfd < ts->sockfd_read)
320 maxfd = ts->sockfd_read;
322 /* Again, sockfd_write == sockfd_read here */
323 if (maxfd < ts->sockfd_write)
324 maxfd = ts->sockfd_write;
330 #else /* !FEATURE_TELNETD_STANDALONE */
332 /* Used in main() only, thus "return 0" actually is exit(0). */
333 #define free_session(ts) return 0
337 static void handle_sigchld(int sig ATTRIBUTE_UNUSED)
342 /* Looping: more than one child may have exited */
344 pid = wait_any_nohang(NULL);
349 if (ts->shell_pid == pid) {
358 int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
359 int telnetd_main(int argc ATTRIBUTE_UNUSED, char **argv)
361 fd_set rdfdset, wrfdset;
365 #if ENABLE_FEATURE_TELNETD_STANDALONE
366 #define IS_INETD (opt & OPT_INETD)
367 int master_fd = master_fd; /* be happy, gcc */
368 unsigned portnbr = 23;
369 char *opt_bindaddr = NULL;
378 /* Even if !STANDALONE, we accept (and ignore) -i, thus people
379 * don't need to guess whether it's ok to pass -i to us */
380 opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"),
381 &issuefile, &loginpath
382 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
383 if (!IS_INETD /*&& !re_execed*/) {
384 /* inform that we start in standalone mode?
385 * May be useful when people forget to give -i */
386 /*bb_error_msg("listening for connections");*/
387 if (!(opt & OPT_FOREGROUND)) {
388 /* DAEMON_CHDIR_ROOT was giving inconsistent
389 * behavior with/without -F, -i */
390 bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
393 /* Redirect log to syslog early, if needed */
394 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
395 openlog(applet_name, 0, LOG_USER);
396 logmode = LOGMODE_SYSLOG;
398 USE_FEATURE_TELNETD_STANDALONE(
400 portnbr = xatou16(opt_portnbr);
403 /* Used to check access(loginpath, X_OK) here. Pointless.
404 * exec will do this for us for free later. */
406 #if ENABLE_FEATURE_TELNETD_STANDALONE
408 sessions = make_new_session(0);
409 if (!sessions) /* pty opening or vfork problem, exit */
410 return 1; /* make_new_session prints error message */
412 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
413 xlisten(master_fd, 1);
416 sessions = make_new_session();
417 if (!sessions) /* pty opening or vfork problem, exit */
418 return 1; /* make_new_session prints error message */
421 /* We don't want to die if just one session is broken */
422 signal(SIGPIPE, SIG_IGN);
424 if (opt & OPT_WATCHCHILD)
425 signal(SIGCHLD, handle_sigchld);
426 else /* prevent dead children from becoming zombies */
427 signal(SIGCHLD, SIG_IGN);
430 This is how the buffers are used. The arrows indicate the movement
432 +-------+ wridx1++ +------+ rdidx1++ +----------+
433 | | <-------------- | buf1 | <-------------- | |
434 | | size1-- +------+ size1++ | |
436 | | rdidx2++ +------+ wridx2++ | |
437 | | --------------> | buf2 | --------------> | |
438 +-------+ size2++ +------+ size2-- +----------+
440 size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
441 size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
443 Each session has got two buffers. Buffers are circular. If sizeN == 0,
444 buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
451 /* Select on the master socket, all telnet sockets and their
452 * ptys if there is room in their session buffers.
453 * NB: scalability problem: we recalculate entire bitmap
454 * before each select. Can be a problem with 500+ connections. */
457 struct tsession *next = ts->next; /* in case we free ts. */
458 if (ts->shell_pid == -1) {
459 /* Child died and we detected that */
462 if (ts->size1 > 0) /* can write to pty */
463 FD_SET(ts->ptyfd, &wrfdset);
464 if (ts->size1 < BUFSIZE) /* can read from socket */
465 FD_SET(ts->sockfd_read, &rdfdset);
466 if (ts->size2 > 0) /* can write to socket */
467 FD_SET(ts->sockfd_write, &wrfdset);
468 if (ts->size2 < BUFSIZE) /* can read from pty */
469 FD_SET(ts->ptyfd, &rdfdset);
474 FD_SET(master_fd, &rdfdset);
475 /* This is needed because free_session() does not
476 * take master_fd into account when it finds new
477 * maxfd among remaining fd's */
478 if (master_fd > maxfd)
482 count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL);
484 goto again; /* EINTR or ENOMEM */
486 #if ENABLE_FEATURE_TELNETD_STANDALONE
487 /* First check for and accept new sessions. */
488 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
490 struct tsession *new_ts;
492 fd = accept(master_fd, NULL, NULL);
495 /* Create a new session and link it into our active list */
496 new_ts = make_new_session(fd);
498 new_ts->next = sessions;
506 /* Then check for data tunneling. */
508 while (ts) { /* For all sessions... */
509 struct tsession *next = ts->next; /* in case we free ts. */
511 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
514 /* Write to pty from buffer 1. */
515 ptr = remove_iacs(ts, &num_totty);
516 count = safe_write(ts->ptyfd, ptr, num_totty);
524 if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
528 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
529 /* Write to socket from buffer 2. */
530 count = MIN(BUFSIZE - ts->wridx2, ts->size2);
531 count = safe_write(ts->sockfd_write, TS_BUF2 + ts->wridx2, count);
539 if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
543 /* Should not be needed, but... remove_iacs is actually buggy
544 * (it cannot process iacs which wrap around buffer's end)!
545 * Since properly fixing it requires writing bigger code,
546 * we rely instead on this code making it virtually impossible
547 * to have wrapped iac (people don't type at 2k/second).
548 * It also allows for bigger reads in common case. */
549 if (ts->size1 == 0) {
553 if (ts->size2 == 0) {
558 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
559 /* Read from socket to buffer 1. */
560 count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
561 count = safe_read(ts->sockfd_read, TS_BUF1 + ts->rdidx1, count);
563 if (count < 0 && errno == EAGAIN)
567 /* Ignore trailing NUL if it is there */
568 if (!TS_BUF1[ts->rdidx1 + count - 1]) {
573 if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
577 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
578 /* Read from pty to buffer 2. */
579 count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
580 count = safe_read(ts->ptyfd, TS_BUF2 + ts->rdidx2, count);
582 if (count < 0 && errno == EAGAIN)
588 if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */