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>
34 #include <sys/syslog.h>
39 #if ENABLE_FEATURE_IPV6
40 typedef struct sockaddr_in6 sockaddr_type;
42 typedef struct sockaddr_in sockaddr_type;
46 static const char *loginpath = "/bin/login";
48 static const char *loginpath = DEFAULT_SHELL;
51 static const char *issuefile = "/etc/issue.net";
53 /* shell name and arguments */
55 static const char *argv_init[2];
57 /* structure that describes a session */
60 struct tsession *next;
61 int sockfd_read, sockfd_write, ptyfd;
63 /* two circular buffers */
65 int rdidx1, wridx1, size1;
66 int rdidx2, wridx2, size2;
70 This is how the buffers are used. The arrows indicate the movement
73 +-------+ wridx1++ +------+ rdidx1++ +----------+
74 | | <-------------- | buf1 | <-------------- | |
75 | | size1-- +------+ size1++ | |
77 | | rdidx2++ +------+ wridx2++ | |
78 | | --------------> | buf2 | --------------> | |
79 +-------+ size2++ +------+ size2-- +----------+
81 Each session has got two buffers.
86 static struct tsession *sessions;
90 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
91 and must be removed so as to not be interpreted by the terminal). Make an
92 uninterrupted string of characters fit for the terminal. Do this by packing
93 all characters meant for the terminal sequentially towards the end of bf.
95 Return a pointer to the beginning of the characters meant for the terminal.
96 and make *num_totty the number of characters that should be sent to
99 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
100 past (bf + len) then that IAC will be left unprocessed and *processed will be
103 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
104 what is the escape character? We aren't handling that situation here.
106 CR-LF ->'s CR mapping is also done here, for convenience
109 remove_iacs(struct tsession *ts, int *pnum_totty)
111 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
112 unsigned char *ptr = ptr0;
113 unsigned char *totty = ptr;
114 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
122 /* We now map \r\n ==> \r for pragmatic reasons.
123 * Many client implementations send \r\n when
124 * the user hits the CarriageReturn key.
126 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
130 * TELOPT_NAWS support!
132 if ((ptr+2) >= end) {
133 /* only the beginning of the IAC is in the
134 buffer we were asked to process, we can't
135 process this char. */
140 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
142 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
145 break; /* incomplete, can't process */
146 ws.ws_col = (ptr[3] << 8) | ptr[4];
147 ws.ws_row = (ptr[5] << 8) | ptr[6];
148 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
151 /* skip 3-byte IAC non-SB cmd */
153 fprintf(stderr, "Ignoring IAC %s,%s\n",
154 TELCMD(ptr[1]), TELOPT(ptr[2]));
161 processed = ptr - ptr0;
162 num_totty = totty - ptr0;
163 /* the difference between processed and num_to tty
164 is all the iacs we removed from the stream.
165 Adjust buf1 accordingly. */
166 ts->wridx1 += processed - num_totty;
167 ts->size1 -= processed - num_totty;
168 *pnum_totty = num_totty;
169 /* move the chars meant for the terminal towards the end of the
171 return memmove(ptr - num_totty, ptr0, num_totty);
176 getpty(char *line, int size)
179 #if ENABLE_FEATURE_DEVPTS
180 p = open("/dev/ptmx", O_RDWR);
187 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
190 safe_strncpy(line, name, size);
198 strcpy(line, "/dev/ptyXX");
200 for (i = 0; i < 16; i++) {
201 line[8] = "pqrstuvwxyzabcde"[i];
203 if (stat(line, &stb) < 0) {
206 for (j = 0; j < 16; j++) {
207 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
209 fprintf(stderr, "Trying to open device: %s\n", line);
210 p = open(line, O_RDWR | O_NOCTTY);
217 #endif /* FEATURE_DEVPTS */
223 send_iac(struct tsession *ts, unsigned char command, int option)
225 /* We rely on that there is space in the buffer for now. */
226 char *b = ts->buf2 + ts->rdidx2;
235 static struct tsession *
237 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
238 SKIP_FEATURE_TELNETD_STANDALONE(void)
240 struct termios termbuf;
243 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
245 ts->buf1 = (char *)(&ts[1]);
246 ts->buf2 = ts->buf1 + BUFSIZE;
248 /* Got a new connection, set up a tty. */
249 fd = getpty(tty_name, 32);
251 bb_error_msg("all terminals in use");
254 if (fd > maxfd) maxfd = fd;
255 ndelay_on(ts->ptyfd = fd);
256 #if ENABLE_FEATURE_TELNETD_STANDALONE
257 if (sock_w > maxfd) maxfd = sock_w;
258 if (sock_r > maxfd) maxfd = sock_r;
259 ndelay_on(ts->sockfd_write = sock_w);
260 ndelay_on(ts->sockfd_read = sock_r);
262 ts->sockfd_write = 1;
263 /* xzalloc: ts->sockfd_read = 0; */
267 /* Make the telnet client understand we will echo characters so it
268 * should not do it locally. We don't tell the client to run linemode,
269 * because we want to handle line editing and tab completion and other
270 * stuff that requires char-by-char support. */
271 send_iac(ts, DO, TELOPT_ECHO);
272 send_iac(ts, DO, TELOPT_NAWS);
273 send_iac(ts, DO, TELOPT_LFLOW);
274 send_iac(ts, WILL, TELOPT_ECHO);
275 send_iac(ts, WILL, TELOPT_SGA);
281 bb_perror_msg("fork");
292 /* open the child's side of the tty. */
293 fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
297 while (fd > 2) close(fd--);
298 /* make new process group */
300 tcsetpgrp(0, getpid());
302 /* The pseudo-terminal allocated to the client is configured to operate in
303 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
304 tcgetattr(0, &termbuf);
305 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
306 termbuf.c_oflag |= ONLCR|XTABS;
307 termbuf.c_iflag |= ICRNL;
308 termbuf.c_iflag &= ~IXOFF;
309 /*termbuf.c_lflag &= ~ICANON;*/
310 tcsetattr(0, TCSANOW, &termbuf);
312 print_login_issue(issuefile, NULL);
314 /* exec shell, with correct argv and env */
315 execv(loginpath, (char *const *)argv_init);
316 bb_perror_msg_and_die("execv");
319 #if ENABLE_FEATURE_TELNETD_STANDALONE
322 free_session(struct tsession *ts)
324 struct tsession *t = sessions;
326 /* unlink this telnet session from the session list */
330 while (t->next != ts)
335 kill(ts->shell_pid, SIGKILL);
336 wait4(ts->shell_pid, NULL, 0, NULL);
338 close(ts->sockfd_read);
339 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
340 close(ts->sockfd_write);
343 /* scan all sessions and find new maxfd */
347 if (maxfd < ts->ptyfd)
349 if (maxfd < ts->sockfd_read)
350 maxfd = ts->sockfd_read;
351 if (maxfd < ts->sockfd_write)
352 maxfd = ts->sockfd_write;
358 dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen)
362 #if ENABLE_FEATURE_IPV6
367 #if ENABLE_FEATURE_IPV6
368 if (socklen >= sizeof(struct sockaddr_in6)
369 && inet_pton(AF_INET6, dotted, &a) > 0
371 ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6;
372 ((struct sockaddr_in6*)sp)->sin6_addr = a.a6;
375 if (socklen >= sizeof(struct sockaddr_in)
376 && inet_pton(AF_INET, dotted, &a) > 0
378 ((struct sockaddr_in*)sp)->sin_family = AF_INET;
379 ((struct sockaddr_in*)sp)->sin_addr = a.a4;
383 return 0; /* success */
387 xsocket_stream_ip4or6(sa_family_t *fp)
389 int fd = socket(AF_INET6, SOCK_STREAM, 0);
390 if (fp) *fp = AF_INET6;
392 fd = xsocket(AF_INET, SOCK_STREAM, 0);
393 if (fp) *fp = AF_INET;
399 create_socket(const char *hostaddr, int port)
401 static const int on = 1;
405 struct sockaddr_in sin;
406 #if ENABLE_FEATURE_IPV6
407 struct sockaddr_in6 sin6;
411 memset(&sa, 0, sizeof(sa));
412 if (hostaddr && dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa)))
415 if (!sa.sa.sa_family)
416 fd = xsocket_stream_ip4or6(&sa.sa.sa_family);
417 else /* user specified -b ADDR dictates family */
418 fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0);
419 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
421 #if ENABLE_FEATURE_IPV6
422 if (sa.sa.sa_family == AF_INET6)
423 sa.sin6.sin6_port = htons(port);
425 if (sa.sa.sa_family == AF_INET)
426 sa.sin.sin_port = htons(port);
428 xbind(fd, &sa.sa, sizeof(sa));
433 #else /* !FEATURE_TELNETD_STANDALONE */
435 /* Never actually called */
436 void free_session(struct tsession *ts);
437 int create_socket(const char *hostaddr, int port);
443 telnetd_main(int argc, char **argv)
445 fd_set rdfdset, wrfdset;
447 int selret, maxlen, w, r;
449 #if ENABLE_FEATURE_TELNETD_STANDALONE
450 #define IS_INETD (opt & OPT_INETD)
451 int master_fd = -1; /* be happy, gcc */
452 unsigned portnbr = 23;
453 char *opt_bindaddr = NULL;
463 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
464 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
465 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
468 opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
469 &issuefile, &loginpath
470 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
471 /* Redirect log to syslog early, if needed */
472 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
473 openlog(applet_name, 0, LOG_USER);
474 logmode = LOGMODE_SYSLOG;
478 USE_FEATURE_TELNETD_STANDALONE(
479 if (opt & OPT_PORT) // -p
480 portnbr = xatou16(opt_portnbr);
482 //if (opt & 0x10) // -F
483 //if (opt & 0x20) // -i
486 /* Used to check access(loginpath, X_OK) here. Pointless.
487 * exec will do this for us for free later. */
488 argv_init[0] = loginpath;
490 #if ENABLE_FEATURE_TELNETD_STANDALONE
492 sessions = make_new_session(0, 1);
494 master_fd = create_socket(opt_bindaddr, portnbr);
495 if (!(opt & OPT_FOREGROUND))
499 sessions = make_new_session();
502 /* We don't want to die if just one session is broken */
503 signal(SIGPIPE, SIG_IGN);
509 FD_SET(master_fd, &rdfdset);
510 /* This is needed because free_session() does not
511 * take into account master_fd when it finds new
512 * maxfd among remaining fd's: */
513 if (master_fd > maxfd)
517 /* select on the master socket, all telnet sockets and their
518 * ptys if there is room in their session buffers. */
521 /* buf1 is used from socket to pty
522 * buf2 is used from pty to socket */
523 if (ts->size1 > 0) /* can write to pty */
524 FD_SET(ts->ptyfd, &wrfdset);
525 if (ts->size1 < BUFSIZE) /* can read from socket */
526 FD_SET(ts->sockfd_read, &rdfdset);
527 if (ts->size2 > 0) /* can write to socket */
528 FD_SET(ts->sockfd_write, &wrfdset);
529 if (ts->size2 < BUFSIZE) /* can read from pty */
530 FD_SET(ts->ptyfd, &rdfdset);
534 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
538 #if ENABLE_FEATURE_TELNETD_STANDALONE
539 /* First check for and accept new sessions. */
540 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
544 struct tsession *new_ts;
547 fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
550 /* Create a new session and link it into our active list */
551 new_ts = make_new_session(fd, fd);
553 new_ts->next = sessions;
561 /* Then check for data tunneling. */
563 while (ts) { /* For all sessions... */
564 struct tsession *next = ts->next; /* in case we free ts. */
566 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
569 /* Write to pty from buffer 1. */
570 ptr = remove_iacs(ts, &num_totty);
571 w = safe_write(ts->ptyfd, ptr, num_totty);
572 /* needed? if (w < 0 && errno == EAGAIN) continue; */
582 if (ts->wridx1 == BUFSIZE)
586 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
587 /* Write to socket from buffer 2. */
588 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
589 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
590 /* needed? if (w < 0 && errno == EAGAIN) continue; */
600 if (ts->wridx2 == BUFSIZE)
604 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
605 /* Read from socket to buffer 1. */
606 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
607 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
608 if (r < 0 && errno == EAGAIN) continue;
616 if (!ts->buf1[ts->rdidx1 + r - 1])
621 if (ts->rdidx1 == BUFSIZE)
625 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
626 /* Read from pty to buffer 2. */
627 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
628 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
629 if (r < 0 && errno == EAGAIN) continue;
639 if (ts->rdidx2 == BUFSIZE)
643 if (ts->size1 == 0) {
647 if (ts->size2 == 0) {