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;
357 #else /* !FEATURE_TELNETD_STANDALONE */
359 /* Never actually called */
360 void free_session(struct tsession *ts);
366 telnetd_main(int argc, char **argv)
368 fd_set rdfdset, wrfdset;
370 int selret, maxlen, w, r;
372 #if ENABLE_FEATURE_TELNETD_STANDALONE
373 #define IS_INETD (opt & OPT_INETD)
374 int master_fd = -1; /* be happy, gcc */
375 unsigned portnbr = 23;
376 char *opt_bindaddr = NULL;
386 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
387 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
388 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
391 opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
392 &issuefile, &loginpath
393 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
394 /* Redirect log to syslog early, if needed */
395 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
396 openlog(applet_name, 0, LOG_USER);
397 logmode = LOGMODE_SYSLOG;
401 USE_FEATURE_TELNETD_STANDALONE(
402 if (opt & OPT_PORT) // -p
403 portnbr = xatou16(opt_portnbr);
405 //if (opt & 0x10) // -F
406 //if (opt & 0x20) // -i
409 /* Used to check access(loginpath, X_OK) here. Pointless.
410 * exec will do this for us for free later. */
411 argv_init[0] = loginpath;
413 #if ENABLE_FEATURE_TELNETD_STANDALONE
415 sessions = make_new_session(0, 1);
417 master_fd = create_and_bind_socket_ip4or6(opt_bindaddr, portnbr);
418 xlisten(master_fd, 1);
419 if (!(opt & OPT_FOREGROUND))
423 sessions = make_new_session();
426 /* We don't want to die if just one session is broken */
427 signal(SIGPIPE, SIG_IGN);
433 FD_SET(master_fd, &rdfdset);
434 /* This is needed because free_session() does not
435 * take into account master_fd when it finds new
436 * maxfd among remaining fd's: */
437 if (master_fd > maxfd)
441 /* select on the master socket, all telnet sockets and their
442 * ptys if there is room in their session buffers. */
445 /* buf1 is used from socket to pty
446 * buf2 is used from pty to socket */
447 if (ts->size1 > 0) /* can write to pty */
448 FD_SET(ts->ptyfd, &wrfdset);
449 if (ts->size1 < BUFSIZE) /* can read from socket */
450 FD_SET(ts->sockfd_read, &rdfdset);
451 if (ts->size2 > 0) /* can write to socket */
452 FD_SET(ts->sockfd_write, &wrfdset);
453 if (ts->size2 < BUFSIZE) /* can read from pty */
454 FD_SET(ts->ptyfd, &rdfdset);
458 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
462 #if ENABLE_FEATURE_TELNETD_STANDALONE
463 /* First check for and accept new sessions. */
464 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
468 struct tsession *new_ts;
471 fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
474 /* Create a new session and link it into our active list */
475 new_ts = make_new_session(fd, fd);
477 new_ts->next = sessions;
485 /* Then check for data tunneling. */
487 while (ts) { /* For all sessions... */
488 struct tsession *next = ts->next; /* in case we free ts. */
490 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
493 /* Write to pty from buffer 1. */
494 ptr = remove_iacs(ts, &num_totty);
495 w = safe_write(ts->ptyfd, ptr, num_totty);
496 /* needed? if (w < 0 && errno == EAGAIN) continue; */
506 if (ts->wridx1 == BUFSIZE)
510 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
511 /* Write to socket from buffer 2. */
512 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
513 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
514 /* needed? if (w < 0 && errno == EAGAIN) continue; */
524 if (ts->wridx2 == BUFSIZE)
528 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
529 /* Read from socket to buffer 1. */
530 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
531 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
532 if (r < 0 && errno == EAGAIN) continue;
540 if (!ts->buf1[ts->rdidx1 + r - 1])
545 if (ts->rdidx1 == BUFSIZE)
549 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
550 /* Read from pty to buffer 2. */
551 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
552 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
553 if (r < 0 && errno == EAGAIN) continue;
563 if (ts->rdidx2 == BUFSIZE)
567 if (ts->size1 == 0) {
571 if (ts->size2 == 0) {