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>
40 static const char *loginpath = "/bin/login";
42 static const char *loginpath = DEFAULT_SHELL;
45 static const char *issuefile = "/etc/issue.net";
47 /* shell name and arguments */
49 static const char *argv_init[2];
51 /* structure that describes a session */
54 struct tsession *next;
55 int sockfd_read, sockfd_write, ptyfd;
57 /* two circular buffers */
59 int rdidx1, wridx1, size1;
60 int rdidx2, wridx2, size2;
64 This is how the buffers are used. The arrows indicate the movement
67 +-------+ wridx1++ +------+ rdidx1++ +----------+
68 | | <-------------- | buf1 | <-------------- | |
69 | | size1-- +------+ size1++ | |
71 | | rdidx2++ +------+ wridx2++ | |
72 | | --------------> | buf2 | --------------> | |
73 +-------+ size2++ +------+ size2-- +----------+
75 Each session has got two buffers.
80 static struct tsession *sessions;
84 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
85 and must be removed so as to not be interpreted by the terminal). Make an
86 uninterrupted string of characters fit for the terminal. Do this by packing
87 all characters meant for the terminal sequentially towards the end of bf.
89 Return a pointer to the beginning of the characters meant for the terminal.
90 and make *num_totty the number of characters that should be sent to
93 Note - If an IAC (3 byte quantity) starts before (bf + len) but extends
94 past (bf + len) then that IAC will be left unprocessed and *processed will be
97 FIXME - if we mean to send 0xFF to the terminal then it will be escaped,
98 what is the escape character? We aren't handling that situation here.
100 CR-LF ->'s CR mapping is also done here, for convenience
103 remove_iacs(struct tsession *ts, int *pnum_totty)
105 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
106 unsigned char *ptr = ptr0;
107 unsigned char *totty = ptr;
108 unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
116 /* We now map \r\n ==> \r for pragmatic reasons.
117 * Many client implementations send \r\n when
118 * the user hits the CarriageReturn key.
120 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
124 * TELOPT_NAWS support!
126 if ((ptr+2) >= end) {
127 /* only the beginning of the IAC is in the
128 buffer we were asked to process, we can't
129 process this char. */
134 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
136 else if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
139 break; /* incomplete, can't process */
140 ws.ws_col = (ptr[3] << 8) | ptr[4];
141 ws.ws_row = (ptr[5] << 8) | ptr[6];
142 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
145 /* skip 3-byte IAC non-SB cmd */
147 fprintf(stderr, "Ignoring IAC %s,%s\n",
148 TELCMD(ptr[1]), TELOPT(ptr[2]));
155 processed = ptr - ptr0;
156 num_totty = totty - ptr0;
157 /* the difference between processed and num_to tty
158 is all the iacs we removed from the stream.
159 Adjust buf1 accordingly. */
160 ts->wridx1 += processed - num_totty;
161 ts->size1 -= processed - num_totty;
162 *pnum_totty = num_totty;
163 /* move the chars meant for the terminal towards the end of the
165 return memmove(ptr - num_totty, ptr0, num_totty);
170 getpty(char *line, int size)
173 #if ENABLE_FEATURE_DEVPTS
174 p = open("/dev/ptmx", O_RDWR);
181 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
184 safe_strncpy(line, name, size);
192 strcpy(line, "/dev/ptyXX");
194 for (i = 0; i < 16; i++) {
195 line[8] = "pqrstuvwxyzabcde"[i];
197 if (stat(line, &stb) < 0) {
200 for (j = 0; j < 16; j++) {
201 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
203 fprintf(stderr, "Trying to open device: %s\n", line);
204 p = open(line, O_RDWR | O_NOCTTY);
211 #endif /* FEATURE_DEVPTS */
217 send_iac(struct tsession *ts, unsigned char command, int option)
219 /* We rely on that there is space in the buffer for now. */
220 char *b = ts->buf2 + ts->rdidx2;
229 static struct tsession *
231 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
232 SKIP_FEATURE_TELNETD_STANDALONE(void)
234 struct termios termbuf;
237 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
239 ts->buf1 = (char *)(&ts[1]);
240 ts->buf2 = ts->buf1 + BUFSIZE;
242 /* Got a new connection, set up a tty. */
243 fd = getpty(tty_name, 32);
245 bb_error_msg("all terminals in use");
248 if (fd > maxfd) maxfd = fd;
249 ndelay_on(ts->ptyfd = fd);
250 #if ENABLE_FEATURE_TELNETD_STANDALONE
251 if (sock_w > maxfd) maxfd = sock_w;
252 if (sock_r > maxfd) maxfd = sock_r;
253 ndelay_on(ts->sockfd_write = sock_w);
254 ndelay_on(ts->sockfd_read = sock_r);
256 ts->sockfd_write = 1;
257 /* xzalloc: ts->sockfd_read = 0; */
261 /* Make the telnet client understand we will echo characters so it
262 * should not do it locally. We don't tell the client to run linemode,
263 * because we want to handle line editing and tab completion and other
264 * stuff that requires char-by-char support. */
265 send_iac(ts, DO, TELOPT_ECHO);
266 send_iac(ts, DO, TELOPT_NAWS);
267 send_iac(ts, DO, TELOPT_LFLOW);
268 send_iac(ts, WILL, TELOPT_ECHO);
269 send_iac(ts, WILL, TELOPT_SGA);
275 bb_perror_msg("fork");
286 /* make new process group */
288 tcsetpgrp(0, getpid());
289 /* ^^^ strace says: "ioctl(0, TIOCSPGRP, [pid]) = -1 ENOTTY" -- ??! */
291 /* open the child's side of the tty. */
292 /* NB: setsid() disconnects from any previous ctty's. Therefore
293 * we must open child's side of the tty AFTER setsid! */
294 fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
298 while (fd > 2) close(fd--);
300 /* The pseudo-terminal allocated to the client is configured to operate in
301 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
302 tcgetattr(0, &termbuf);
303 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
304 termbuf.c_oflag |= ONLCR|XTABS;
305 termbuf.c_iflag |= ICRNL;
306 termbuf.c_iflag &= ~IXOFF;
307 /*termbuf.c_lflag &= ~ICANON;*/
308 tcsetattr(0, TCSANOW, &termbuf);
310 print_login_issue(issuefile, NULL);
312 /* exec shell, with correct argv and env */
313 execv(loginpath, (char *const *)argv_init);
314 bb_perror_msg_and_die("execv");
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(argc, 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. */
409 argv_init[0] = loginpath;
411 #if ENABLE_FEATURE_TELNETD_STANDALONE
413 sessions = make_new_session(0, 1);
415 master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
416 xlisten(master_fd, 1);
417 if (!(opt & OPT_FOREGROUND))
418 bb_daemonize(DAEMON_CHDIR_ROOT);
421 sessions = make_new_session();
424 /* We don't want to die if just one session is broken */
425 signal(SIGPIPE, SIG_IGN);
431 FD_SET(master_fd, &rdfdset);
432 /* This is needed because free_session() does not
433 * take into account master_fd when it finds new
434 * maxfd among remaining fd's: */
435 if (master_fd > maxfd)
439 /* select on the master socket, all telnet sockets and their
440 * ptys if there is room in their session buffers. */
443 /* buf1 is used from socket to pty
444 * buf2 is used from pty to socket */
445 if (ts->size1 > 0) /* can write to pty */
446 FD_SET(ts->ptyfd, &wrfdset);
447 if (ts->size1 < BUFSIZE) /* can read from socket */
448 FD_SET(ts->sockfd_read, &rdfdset);
449 if (ts->size2 > 0) /* can write to socket */
450 FD_SET(ts->sockfd_write, &wrfdset);
451 if (ts->size2 < BUFSIZE) /* can read from pty */
452 FD_SET(ts->ptyfd, &rdfdset);
456 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
460 #if ENABLE_FEATURE_TELNETD_STANDALONE
461 /* First check for and accept new sessions. */
462 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
464 struct tsession *new_ts;
466 fd = accept(master_fd, NULL, 0);
469 /* Create a new session and link it into our active list */
470 new_ts = make_new_session(fd, fd);
472 new_ts->next = sessions;
480 /* Then check for data tunneling. */
482 while (ts) { /* For all sessions... */
483 struct tsession *next = ts->next; /* in case we free ts. */
485 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
488 /* Write to pty from buffer 1. */
489 ptr = remove_iacs(ts, &num_totty);
490 w = safe_write(ts->ptyfd, ptr, num_totty);
491 /* needed? if (w < 0 && errno == EAGAIN) continue; */
501 if (ts->wridx1 == BUFSIZE)
505 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
506 /* Write to socket from buffer 2. */
507 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
508 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
509 /* needed? if (w < 0 && errno == EAGAIN) continue; */
519 if (ts->wridx2 == BUFSIZE)
523 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
524 /* Read from socket to buffer 1. */
525 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
526 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
527 if (r < 0 && errno == EAGAIN) continue;
535 if (!ts->buf1[ts->rdidx1 + r - 1])
540 if (ts->rdidx1 == BUFSIZE)
544 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
545 /* Read from pty to buffer 2. */
546 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
547 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
548 if (r < 0 && errno == EAGAIN) continue;
558 if (ts->rdidx2 == BUFSIZE)
562 if (ts->size1 == 0) {
566 if (ts->size2 == 0) {