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 #define SOCKET_TYPE AF_INET6
41 typedef struct sockaddr_in6 sockaddr_type;
43 #define SOCKET_TYPE AF_INET
44 typedef struct sockaddr_in sockaddr_type;
48 static const char *loginpath = "/bin/login";
50 static const char *loginpath = DEFAULT_SHELL;
53 static const char *issuefile = "/etc/issue.net";
55 /* shell name and arguments */
57 static const char *argv_init[2];
59 /* structure that describes a session */
62 struct tsession *next;
63 int sockfd_read, sockfd_write, ptyfd;
65 /* two circular buffers */
67 int rdidx1, wridx1, size1;
68 int rdidx2, wridx2, size2;
72 This is how the buffers are used. The arrows indicate the movement
75 +-------+ wridx1++ +------+ rdidx1++ +----------+
76 | | <-------------- | buf1 | <-------------- | |
77 | | size1-- +------+ size1++ | |
79 | | rdidx2++ +------+ wridx2++ | |
80 | | --------------> | buf2 | --------------> | |
81 +-------+ size2++ +------+ size2-- +----------+
83 Each session has got two buffers.
88 static struct tsession *sessions;
92 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
93 and must be removed so as to not be interpreted by the terminal). Make an
94 uninterrupted string of characters fit for the terminal. Do this by packing
95 all characters meant for the terminal sequentially towards the end of bf.
97 Return a pointer to the beginning of the characters meant for the terminal.
98 and make *num_totty the number of characters that should be sent to
101 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
102 past (bf + len) then that IAC will be left unprocessed and *processed will be
105 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
106 what is the escape character? We aren't handling that situation here.
108 CR-LF ->'s CR mapping is also done here, for convenience
111 remove_iacs(struct tsession *ts, int *pnum_totty)
113 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
114 unsigned char *ptr = ptr0;
115 unsigned char *totty = ptr;
116 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
124 /* We now map \r\n ==> \r for pragmatic reasons.
125 * Many client implementations send \r\n when
126 * the user hits the CarriageReturn key.
128 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
132 * TELOPT_NAWS support!
134 if ((ptr+2) >= end) {
135 /* only the beginning of the IAC is in the
136 buffer we were asked to process, we can't
137 process this char. */
142 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
144 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
147 break; /* incomplete, can't process */
148 ws.ws_col = (ptr[3] << 8) | ptr[4];
149 ws.ws_row = (ptr[5] << 8) | ptr[6];
150 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
153 /* skip 3-byte IAC non-SB cmd */
155 fprintf(stderr, "Ignoring IAC %s,%s\n",
156 TELCMD(ptr[1]), TELOPT(ptr[2]));
163 processed = ptr - ptr0;
164 num_totty = totty - ptr0;
165 /* the difference between processed and num_to tty
166 is all the iacs we removed from the stream.
167 Adjust buf1 accordingly. */
168 ts->wridx1 += processed - num_totty;
169 ts->size1 -= processed - num_totty;
170 *pnum_totty = num_totty;
171 /* move the chars meant for the terminal towards the end of the
173 return memmove(ptr - num_totty, ptr0, num_totty);
178 getpty(char *line, int size)
181 #if ENABLE_FEATURE_DEVPTS
182 p = open("/dev/ptmx", O_RDWR);
189 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
192 safe_strncpy(line, name, size);
200 strcpy(line, "/dev/ptyXX");
202 for (i = 0; i < 16; i++) {
203 line[8] = "pqrstuvwxyzabcde"[i];
205 if (stat(line, &stb) < 0) {
208 for (j = 0; j < 16; j++) {
209 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
211 fprintf(stderr, "Trying to open device: %s\n", line);
212 p = open(line, O_RDWR | O_NOCTTY);
219 #endif /* FEATURE_DEVPTS */
225 send_iac(struct tsession *ts, unsigned char command, int option)
227 /* We rely on that there is space in the buffer for now. */
228 char *b = ts->buf2 + ts->rdidx2;
237 static struct tsession *
239 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
240 SKIP_FEATURE_TELNETD_STANDALONE(void)
242 struct termios termbuf;
245 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
247 ts->buf1 = (char *)(&ts[1]);
248 ts->buf2 = ts->buf1 + BUFSIZE;
250 /* Got a new connection, set up a tty. */
251 fd = getpty(tty_name, 32);
253 bb_error_msg("all terminals in use");
256 if (fd > maxfd) maxfd = fd;
257 ndelay_on(ts->ptyfd = fd);
258 #if ENABLE_FEATURE_TELNETD_STANDALONE
259 if (sock_w > maxfd) maxfd = sock_w;
260 if (sock_r > maxfd) maxfd = sock_r;
261 ndelay_on(ts->sockfd_write = sock_w);
262 ndelay_on(ts->sockfd_read = sock_r);
264 ts->sockfd_write = 1;
265 /* xzalloc: ts->sockfd_read = 0; */
269 /* Make the telnet client understand we will echo characters so it
270 * should not do it locally. We don't tell the client to run linemode,
271 * because we want to handle line editing and tab completion and other
272 * stuff that requires char-by-char support. */
273 send_iac(ts, DO, TELOPT_ECHO);
274 send_iac(ts, DO, TELOPT_NAWS);
275 send_iac(ts, DO, TELOPT_LFLOW);
276 send_iac(ts, WILL, TELOPT_ECHO);
277 send_iac(ts, WILL, TELOPT_SGA);
283 bb_perror_msg("fork");
294 /* open the child's side of the tty. */
295 fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
299 while (fd > 2) close(fd--);
300 /* make new process group */
302 tcsetpgrp(0, getpid());
304 /* The pseudo-terminal allocated to the client is configured to operate in
305 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
306 tcgetattr(0, &termbuf);
307 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
308 termbuf.c_oflag |= ONLCR|XTABS;
309 termbuf.c_iflag |= ICRNL;
310 termbuf.c_iflag &= ~IXOFF;
311 /*termbuf.c_lflag &= ~ICANON;*/
312 tcsetattr(0, TCSANOW, &termbuf);
314 print_login_issue(issuefile, NULL);
316 /* exec shell, with correct argv and env */
317 execv(loginpath, (char *const *)argv_init);
318 bb_perror_msg_and_die("execv");
321 #if ENABLE_FEATURE_TELNETD_STANDALONE
324 free_session(struct tsession *ts)
326 struct tsession *t = sessions;
328 /* unlink this telnet session from the session list */
332 while (t->next != ts)
337 kill(ts->shell_pid, SIGKILL);
338 wait4(ts->shell_pid, NULL, 0, NULL);
340 close(ts->sockfd_read);
341 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
342 close(ts->sockfd_write);
345 /* scan all sessions and find new maxfd */
349 if (maxfd < ts->ptyfd)
351 if (maxfd < ts->sockfd_read)
352 maxfd = ts->sockfd_read;
353 if (maxfd < ts->sockfd_write)
354 maxfd = ts->sockfd_write;
360 create_socket(int port, const char *opt_bindaddr)
362 static const int on = 1;
365 #if !ENABLE_FEATURE_IPV6
366 struct in_addr bind_addr = { .s_addr = 0x0 };
368 /* TODO: generic string -> sockaddr converter */
369 if (opt_bindaddr && inet_aton(opt_bindaddr, &bind_addr) == 0)
372 fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
373 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
374 memset((void *)&sa, 0, sizeof(sa));
375 #if ENABLE_FEATURE_IPV6
376 sa.sin6_family = AF_INET6;
377 sa.sin6_port = htons(port);
378 /* sa.sin6_addr = bind_addr6; */
380 sa.sin_family = AF_INET;
381 sa.sin_port = htons(port);
382 sa.sin_addr = bind_addr;
384 xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
389 #else /* !FEATURE_TELNETD_STANDALONE */
391 /* Never actually called */
392 void free_session(struct tsession *ts);
393 int create_socket(int port, const char *opt_bindaddr);
399 telnetd_main(int argc, char **argv)
401 fd_set rdfdset, wrfdset;
403 int selret, maxlen, w, r;
405 #if ENABLE_FEATURE_TELNETD_STANDALONE
406 #define IS_INETD (opt & OPT_INETD)
407 int master_fd = -1; /* be happy, gcc */
408 unsigned portnbr = 23;
409 char *opt_bindaddr = NULL;
419 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
420 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
421 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
424 opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
425 &issuefile, &loginpath
426 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
427 /* Redirect log to syslog early, if needed */
428 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
429 openlog(applet_name, 0, LOG_USER);
430 logmode = LOGMODE_SYSLOG;
434 USE_FEATURE_TELNETD_STANDALONE(
435 if (opt & OPT_PORT) // -p
436 portnbr = xatou16(opt_portnbr);
438 //if (opt & 0x10) // -F
439 //if (opt & 0x20) // -i
442 /* Used to check access(loginpath, X_OK) here. Pointless.
443 * exec will do this for us for free later. */
444 argv_init[0] = loginpath;
446 #if ENABLE_FEATURE_TELNETD_STANDALONE
448 sessions = make_new_session(0, 1);
450 master_fd = create_socket(portnbr, opt_bindaddr);
451 if (!(opt & OPT_FOREGROUND))
455 sessions = make_new_session();
458 /* We don't want to die if just one session is broken */
459 signal(SIGPIPE, SIG_IGN);
465 FD_SET(master_fd, &rdfdset);
466 /* This is needed because free_session() does not
467 * take into account master_fd when it finds new
468 * maxfd among remaining fd's: */
469 if (master_fd > maxfd)
473 /* select on the master socket, all telnet sockets and their
474 * ptys if there is room in their session buffers. */
477 /* buf1 is used from socket to pty
478 * buf2 is used from pty to socket */
479 if (ts->size1 > 0) /* can write to pty */
480 FD_SET(ts->ptyfd, &wrfdset);
481 if (ts->size1 < BUFSIZE) /* can read from socket */
482 FD_SET(ts->sockfd_read, &rdfdset);
483 if (ts->size2 > 0) /* can write to socket */
484 FD_SET(ts->sockfd_write, &wrfdset);
485 if (ts->size2 < BUFSIZE) /* can read from pty */
486 FD_SET(ts->ptyfd, &rdfdset);
490 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
494 #if ENABLE_FEATURE_TELNETD_STANDALONE
495 /* First check for and accept new sessions. */
496 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
500 struct tsession *new_ts;
503 fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
506 /* Create a new session and link it into our active list */
507 new_ts = make_new_session(fd, fd);
509 new_ts->next = sessions;
517 /* Then check for data tunneling. */
519 while (ts) { /* For all sessions... */
520 struct tsession *next = ts->next; /* in case we free ts. */
522 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
525 /* Write to pty from buffer 1. */
526 ptr = remove_iacs(ts, &num_totty);
527 w = safe_write(ts->ptyfd, ptr, num_totty);
528 /* needed? if (w < 0 && errno == EAGAIN) continue; */
538 if (ts->wridx1 == BUFSIZE)
542 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
543 /* Write to socket from buffer 2. */
544 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
545 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
546 /* needed? if (w < 0 && errno == EAGAIN) continue; */
556 if (ts->wridx2 == BUFSIZE)
560 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
561 /* Read from socket to buffer 1. */
562 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
563 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
564 if (r < 0 && errno == EAGAIN) continue;
572 if (!ts->buf1[ts->rdidx1 + r - 1])
577 if (ts->rdidx1 == BUFSIZE)
581 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
582 /* Read from pty to buffer 2. */
583 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
584 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
585 if (r < 0 && errno == EAGAIN) continue;
595 if (ts->rdidx2 == BUFSIZE)
599 if (ts->size1 == 0) {
603 if (ts->size2 == 0) {