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
32 #include <arpa/telnet.h>
33 #include <sys/syslog.h>
37 static const char *loginpath = "/bin/login";
39 static const char *loginpath = DEFAULT_SHELL;
42 static const char *issuefile = "/etc/issue.net";
44 /* structure that describes a session */
47 struct tsession *next;
48 int sockfd_read, sockfd_write, ptyfd;
50 /* two circular buffers */
52 int rdidx1, wridx1, size1;
53 int rdidx2, wridx2, size2;
56 /* Two buffers are directly after tsession in malloced memory.
57 * Make whole thing fit in 4k */
58 enum { BUFSIZE = (4*1024 - sizeof(struct tsession)) / 2 };
61 This is how the buffers are used. The arrows indicate the movement
64 +-------+ wridx1++ +------+ rdidx1++ +----------+
65 | | <-------------- | buf1 | <-------------- | |
66 | | size1-- +------+ size1++ | |
68 | | rdidx2++ +------+ wridx2++ | |
69 | | --------------> | buf2 | --------------> | |
70 +-------+ size2++ +------+ size2-- +----------+
72 Each session has got two buffers.
77 static struct tsession *sessions;
81 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
82 and must be removed so as to not be interpreted by the terminal). Make an
83 uninterrupted string of characters fit for the terminal. Do this by packing
84 all characters meant for the terminal sequentially towards the end of bf.
86 Return a pointer to the beginning of the characters meant for the terminal.
87 and make *num_totty the number of characters that should be sent to
90 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
91 past (bf + len) then that IAC will be left unprocessed and *processed will be
94 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
95 what is the escape character? We aren't handling that situation here.
97 CR-LF ->'s CR mapping is also done here, for convenience
100 remove_iacs(struct tsession *ts, int *pnum_totty)
102 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
103 unsigned char *ptr = ptr0;
104 unsigned char *totty = ptr;
105 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
113 /* We now map \r\n ==> \r for pragmatic reasons.
114 * Many client implementations send \r\n when
115 * the user hits the CarriageReturn key.
117 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
121 * TELOPT_NAWS support!
123 if ((ptr+2) >= end) {
124 /* only the beginning of the IAC is in the
125 buffer we were asked to process, we can't
126 process this char. */
131 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
133 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
136 break; /* incomplete, can't process */
137 ws.ws_col = (ptr[3] << 8) | ptr[4];
138 ws.ws_row = (ptr[5] << 8) | ptr[6];
139 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
142 /* skip 3-byte IAC non-SB cmd */
144 fprintf(stderr, "Ignoring IAC %s,%s\n",
145 TELCMD(ptr[1]), TELOPT(ptr[2]));
152 processed = ptr - ptr0;
153 num_totty = totty - ptr0;
154 /* the difference between processed and num_to tty
155 is all the iacs we removed from the stream.
156 Adjust buf1 accordingly. */
157 ts->wridx1 += processed - num_totty;
158 ts->size1 -= processed - num_totty;
159 *pnum_totty = num_totty;
160 /* move the chars meant for the terminal towards the end of the
162 return memmove(ptr - num_totty, ptr0, num_totty);
167 getpty(char *line, int size)
170 #if ENABLE_FEATURE_DEVPTS
171 p = open("/dev/ptmx", O_RDWR);
178 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
181 safe_strncpy(line, name, size);
189 strcpy(line, "/dev/ptyXX");
191 for (i = 0; i < 16; i++) {
192 line[8] = "pqrstuvwxyzabcde"[i];
194 if (stat(line, &stb) < 0) {
197 for (j = 0; j < 16; j++) {
198 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
200 fprintf(stderr, "Trying to open device: %s\n", line);
201 p = open(line, O_RDWR | O_NOCTTY);
208 #endif /* FEATURE_DEVPTS */
214 send_iac(struct tsession *ts, unsigned char command, int option)
216 /* We rely on that there is space in the buffer for now. */
217 char *b = ts->buf2 + ts->rdidx2;
226 static struct tsession *
228 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
229 SKIP_FEATURE_TELNETD_STANDALONE(void)
231 const char *login_argv[2];
232 struct termios termbuf;
235 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
237 ts->buf1 = (char *)(&ts[1]);
238 ts->buf2 = ts->buf1 + BUFSIZE;
240 /* Got a new connection, set up a tty. */
241 fd = getpty(tty_name, 32);
243 bb_error_msg("all terminals in use");
246 if (fd > maxfd) maxfd = fd;
247 ndelay_on(ts->ptyfd = fd);
248 #if ENABLE_FEATURE_TELNETD_STANDALONE
249 if (sock_w > maxfd) maxfd = sock_w;
250 if (sock_r > maxfd) maxfd = sock_r;
251 ndelay_on(ts->sockfd_write = sock_w);
252 ndelay_on(ts->sockfd_read = sock_r);
254 ts->sockfd_write = 1;
255 /* xzalloc: ts->sockfd_read = 0; */
259 /* Make the telnet client understand we will echo characters so it
260 * should not do it locally. We don't tell the client to run linemode,
261 * because we want to handle line editing and tab completion and other
262 * stuff that requires char-by-char support. */
263 send_iac(ts, DO, TELOPT_ECHO);
264 send_iac(ts, DO, TELOPT_NAWS);
265 send_iac(ts, DO, TELOPT_LFLOW);
266 send_iac(ts, WILL, TELOPT_ECHO);
267 send_iac(ts, WILL, TELOPT_SGA);
273 bb_perror_msg("fork");
284 /* make new session and process group */
287 /* open the child's side of the tty. */
288 /* NB: setsid() disconnects from any previous ctty's. Therefore
289 * we must open child's side of the tty AFTER setsid! */
290 fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
294 while (fd > 2) close(fd--);
295 tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
297 /* The pseudo-terminal allocated to the client is configured to operate in
298 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
299 tcgetattr(0, &termbuf);
300 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
301 termbuf.c_oflag |= ONLCR|XTABS;
302 termbuf.c_iflag |= ICRNL;
303 termbuf.c_iflag &= ~IXOFF;
304 /*termbuf.c_lflag &= ~ICANON;*/
305 tcsetattr(0, TCSANOW, &termbuf);
307 print_login_issue(issuefile, NULL);
309 /* exec shell / login /whatever */
310 login_argv[0] = loginpath;
311 login_argv[1] = NULL;
312 execv(loginpath, (char **)login_argv);
313 /* Hmmm... this gets sent to the client thru fd#2! Is it ok?? */
314 bb_perror_msg_and_die("execv %s", loginpath);
317 #if ENABLE_FEATURE_TELNETD_STANDALONE
320 free_session(struct tsession *ts)
322 struct tsession *t = sessions;
324 /* unlink this telnet session from the session list */
328 while (t->next != ts)
333 kill(ts->shell_pid, SIGKILL);
334 wait4(ts->shell_pid, NULL, 0, NULL);
336 close(ts->sockfd_read);
337 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
338 close(ts->sockfd_write);
341 /* scan all sessions and find new maxfd */
345 if (maxfd < ts->ptyfd)
347 if (maxfd < ts->sockfd_read)
348 maxfd = ts->sockfd_read;
349 if (maxfd < ts->sockfd_write)
350 maxfd = ts->sockfd_write;
355 #else /* !FEATURE_TELNETD_STANDALONE */
357 /* Never actually called */
358 void free_session(struct tsession *ts);
363 int telnetd_main(int argc, char **argv);
364 int telnetd_main(int argc, char **argv)
366 fd_set rdfdset, wrfdset;
368 int selret, maxlen, w, r;
370 #if ENABLE_FEATURE_TELNETD_STANDALONE
371 #define IS_INETD (opt & OPT_INETD)
372 int master_fd = -1; /* be happy, gcc */
373 unsigned portnbr = 23;
374 char *opt_bindaddr = NULL;
384 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
385 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
386 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
389 opt = getopt32(argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
390 &issuefile, &loginpath
391 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
392 /* Redirect log to syslog early, if needed */
393 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
394 openlog(applet_name, 0, LOG_USER);
395 logmode = LOGMODE_SYSLOG;
399 USE_FEATURE_TELNETD_STANDALONE(
400 if (opt & OPT_PORT) // -p
401 portnbr = xatou16(opt_portnbr);
403 //if (opt & 0x10) // -F
404 //if (opt & 0x20) // -i
407 /* Used to check access(loginpath, X_OK) here. Pointless.
408 * exec will do this for us for free later. */
410 #if ENABLE_FEATURE_TELNETD_STANDALONE
412 sessions = make_new_session(0, 1);
414 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
415 xlisten(master_fd, 1);
416 if (!(opt & OPT_FOREGROUND))
417 bb_daemonize(DAEMON_CHDIR_ROOT);
420 sessions = make_new_session();
423 /* We don't want to die if just one session is broken */
424 signal(SIGPIPE, SIG_IGN);
430 FD_SET(master_fd, &rdfdset);
431 /* This is needed because free_session() does not
432 * take into account master_fd when it finds new
433 * maxfd among remaining fd's: */
434 if (master_fd > maxfd)
438 /* select on the master socket, all telnet sockets and their
439 * ptys if there is room in their session buffers. */
442 /* buf1 is used from socket to pty
443 * buf2 is used from pty to socket */
444 if (ts->size1 > 0) /* can write to pty */
445 FD_SET(ts->ptyfd, &wrfdset);
446 if (ts->size1 < BUFSIZE) /* can read from socket */
447 FD_SET(ts->sockfd_read, &rdfdset);
448 if (ts->size2 > 0) /* can write to socket */
449 FD_SET(ts->sockfd_write, &wrfdset);
450 if (ts->size2 < BUFSIZE) /* can read from pty */
451 FD_SET(ts->ptyfd, &rdfdset);
455 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
459 #if ENABLE_FEATURE_TELNETD_STANDALONE
460 /* First check for and accept new sessions. */
461 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
463 struct tsession *new_ts;
465 fd = accept(master_fd, NULL, 0);
468 /* Create a new session and link it into our active list */
469 new_ts = make_new_session(fd, fd);
471 new_ts->next = sessions;
479 /* Then check for data tunneling. */
481 while (ts) { /* For all sessions... */
482 struct tsession *next = ts->next; /* in case we free ts. */
484 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
487 /* Write to pty from buffer 1. */
488 ptr = remove_iacs(ts, &num_totty);
489 w = safe_write(ts->ptyfd, ptr, num_totty);
490 /* needed? if (w < 0 && errno == EAGAIN) continue; */
500 if (ts->wridx1 == BUFSIZE)
504 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
505 /* Write to socket from buffer 2. */
506 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
507 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
508 /* needed? if (w < 0 && errno == EAGAIN) continue; */
518 if (ts->wridx2 == BUFSIZE)
522 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
523 /* Read from socket to buffer 1. */
524 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
525 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
526 if (r < 0 && errno == EAGAIN) continue;
534 if (!ts->buf1[ts->rdidx1 + r - 1])
539 if (ts->rdidx1 == BUFSIZE)
543 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
544 /* Read from pty to buffer 2. */
545 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
546 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
547 if (r < 0 && errno == EAGAIN) continue;
557 if (ts->rdidx2 == BUFSIZE)
561 if (ts->size1 == 0) {
565 if (ts->size2 == 0) {