13c36aa4645ffdf32bbd0c983db82e20c204f29b
[oweals/busybox.git] / networking / telnetd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Simple telnet server
4  * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
5  *
6  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7  *
8  * ---------------------------------------------------------------------------
9  * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
10  ****************************************************************************
11  *
12  * The telnetd manpage says it all:
13  *
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.
19  *
20  * Vladimir Oleynik <dzo@simtreas.ru> 2001
21  * Set process group corrections, initial busybox port
22  */
23
24 //usage:#define telnetd_trivial_usage
25 //usage:       "[OPTIONS]"
26 //usage:#define telnetd_full_usage "\n\n"
27 //usage:       "Handle incoming telnet connections"
28 //usage:        IF_NOT_FEATURE_TELNETD_STANDALONE(" via inetd") "\n"
29 //usage:     "\n        -l LOGIN        Exec LOGIN on connect"
30 //usage:     "\n        -f ISSUE_FILE   Display ISSUE_FILE instead of /etc/issue"
31 //usage:     "\n        -K              Close connection as soon as login exits"
32 //usage:     "\n                        (normally wait until all programs close slave pty)"
33 //usage:        IF_FEATURE_TELNETD_STANDALONE(
34 //usage:     "\n        -p PORT         Port to listen on"
35 //usage:     "\n        -b ADDR[:PORT]  Address to bind to"
36 //usage:     "\n        -F              Run in foreground"
37 //usage:     "\n        -i              Inetd mode"
38 //usage:        IF_FEATURE_TELNETD_INETD_WAIT(
39 //usage:     "\n        -w SEC          Inetd 'wait' mode, linger time SEC"
40 //usage:     "\n        -S              Log to syslog (implied by -i or without -F and -w)"
41 //usage:        )
42 //usage:        )
43
44 #define DEBUG 0
45
46 #include "libbb.h"
47 #include "common_bufsiz.h"
48 #include <syslog.h>
49
50 #if DEBUG
51 # define TELCMDS
52 # define TELOPTS
53 #endif
54 #include <arpa/telnet.h>
55
56
57 struct tsession {
58         struct tsession *next;
59         pid_t shell_pid;
60         int sockfd_read;
61         int sockfd_write;
62         int ptyfd;
63
64         /* two circular buffers */
65         /*char *buf1, *buf2;*/
66 /*#define TS_BUF1(ts) ts->buf1*/
67 /*#define TS_BUF2(ts) TS_BUF2(ts)*/
68 #define TS_BUF1(ts) ((unsigned char*)(ts + 1))
69 #define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
70         int rdidx1, wridx1, size1;
71         int rdidx2, wridx2, size2;
72 };
73
74 /* Two buffers are directly after tsession in malloced memory.
75  * Make whole thing fit in 4k */
76 enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
77
78
79 /* Globals */
80 struct globals {
81         struct tsession *sessions;
82         const char *loginpath;
83         const char *issuefile;
84         int maxfd;
85 } FIX_ALIASING;
86 #define G (*(struct globals*)bb_common_bufsiz1)
87 #define INIT_G() do { \
88         setup_common_bufsiz(); \
89         G.loginpath = "/bin/login"; \
90         G.issuefile = "/etc/issue.net"; \
91 } while (0)
92
93
94 /*
95    Remove all IAC's from buf1 (received IACs are ignored and must be removed
96    so as to not be interpreted by the terminal).  Make an uninterrupted
97    string of characters fit for the terminal.  Do this by packing
98    all characters meant for the terminal sequentially towards the end of buf.
99
100    Return a pointer to the beginning of the characters meant for the terminal
101    and make *num_totty the number of characters that should be sent to
102    the terminal.
103
104    Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
105    past (bf + len) then that IAC will be left unprocessed and *processed
106    will be less than len.
107
108    CR-LF ->'s CR mapping is also done here, for convenience.
109
110    NB: may fail to remove iacs which wrap around buffer!
111  */
112 static unsigned char *
113 remove_iacs(struct tsession *ts, int *pnum_totty)
114 {
115         unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
116         unsigned char *ptr = ptr0;
117         unsigned char *totty = ptr;
118         unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
119         int num_totty;
120
121         while (ptr < end) {
122                 if (*ptr != IAC) {
123                         char c = *ptr;
124
125                         *totty++ = c;
126                         ptr++;
127                         /* We map \r\n ==> \r for pragmatic reasons.
128                          * Many client implementations send \r\n when
129                          * the user hits the CarriageReturn key.
130                          * See RFC 1123 3.3.1 Telnet End-of-Line Convention.
131                          */
132                         if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
133                                 ptr++;
134                         continue;
135                 }
136
137                 if ((ptr+1) >= end)
138                         break;
139                 if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
140                         ptr += 2;
141                         continue;
142                 }
143                 if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
144                         *totty++ = ptr[1];
145                         ptr += 2;
146                         continue;
147                 }
148
149                 /*
150                  * TELOPT_NAWS support!
151                  */
152                 if ((ptr+2) >= end) {
153                         /* Only the beginning of the IAC is in the
154                         buffer we were asked to process, we can't
155                         process this char */
156                         break;
157                 }
158                 /*
159                  * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
160                  */
161                 if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
162                         struct winsize ws;
163                         if ((ptr+8) >= end)
164                                 break;  /* incomplete, can't process */
165                         ws.ws_col = (ptr[3] << 8) | ptr[4];
166                         ws.ws_row = (ptr[5] << 8) | ptr[6];
167                         ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
168                         ptr += 9;
169                         continue;
170                 }
171                 /* skip 3-byte IAC non-SB cmd */
172 #if DEBUG
173                 fprintf(stderr, "Ignoring IAC %s,%s\n",
174                                 TELCMD(ptr[1]), TELOPT(ptr[2]));
175 #endif
176                 ptr += 3;
177         }
178
179         num_totty = totty - ptr0;
180         *pnum_totty = num_totty;
181         /* The difference between ptr and totty is number of iacs
182            we removed from the stream. Adjust buf1 accordingly */
183         if ((ptr - totty) == 0) /* 99.999% of cases */
184                 return ptr0;
185         ts->wridx1 += ptr - totty;
186         ts->size1 -= ptr - totty;
187         /* Move chars meant for the terminal towards the end of the buffer */
188         return memmove(ptr - num_totty, ptr0, num_totty);
189 }
190
191 /*
192  * Converting single IAC into double on output
193  */
194 static size_t iac_safe_write(int fd, const char *buf, size_t count)
195 {
196         const char *IACptr;
197         size_t wr, rc, total;
198
199         total = 0;
200         while (1) {
201                 if (count == 0)
202                         return total;
203                 if (*buf == (char)IAC) {
204                         static const char IACIAC[] ALIGN1 = { IAC, IAC };
205                         rc = safe_write(fd, IACIAC, 2);
206                         if (rc != 2)
207                                 break;
208                         buf++;
209                         total++;
210                         count--;
211                         continue;
212                 }
213                 /* count != 0, *buf != IAC */
214                 IACptr = memchr(buf, IAC, count);
215                 wr = count;
216                 if (IACptr)
217                         wr = IACptr - buf;
218                 rc = safe_write(fd, buf, wr);
219                 if (rc != wr)
220                         break;
221                 buf += rc;
222                 total += rc;
223                 count -= rc;
224         }
225         /* here: rc - result of last short write */
226         if ((ssize_t)rc < 0) { /* error? */
227                 if (total == 0)
228                         return rc;
229                 rc = 0;
230         }
231         return total + rc;
232 }
233
234 /* Must match getopt32 string */
235 enum {
236         OPT_WATCHCHILD = (1 << 2), /* -K */
237         OPT_INETD      = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
238         OPT_PORT       = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
239         OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
240         OPT_SYSLOG     = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
241         OPT_WAIT       = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
242 };
243
244 static struct tsession *
245 make_new_session(
246                 IF_FEATURE_TELNETD_STANDALONE(int sock)
247                 IF_NOT_FEATURE_TELNETD_STANDALONE(void)
248 ) {
249 #if !ENABLE_FEATURE_TELNETD_STANDALONE
250         enum { sock = 0 };
251 #endif
252         const char *login_argv[2];
253         struct termios termbuf;
254         int fd, pid;
255         char tty_name[GETPTY_BUFSIZE];
256         struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
257
258         /*ts->buf1 = (char *)(ts + 1);*/
259         /*ts->buf2 = ts->buf1 + BUFSIZE;*/
260
261         /* Got a new connection, set up a tty */
262         fd = xgetpty(tty_name);
263         if (fd > G.maxfd)
264                 G.maxfd = fd;
265         ts->ptyfd = fd;
266         ndelay_on(fd);
267         close_on_exec_on(fd);
268
269         /* SO_KEEPALIVE by popular demand */
270         setsockopt_keepalive(sock);
271 #if ENABLE_FEATURE_TELNETD_STANDALONE
272         ts->sockfd_read = sock;
273         ndelay_on(sock);
274         if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
275                 sock++; /* so use fd 1 for output */
276                 ndelay_on(sock);
277         }
278         ts->sockfd_write = sock;
279         if (sock > G.maxfd)
280                 G.maxfd = sock;
281 #else
282         /* ts->sockfd_read = 0; - done by xzalloc */
283         ts->sockfd_write = 1;
284         ndelay_on(0);
285         ndelay_on(1);
286 #endif
287
288         /* Make the telnet client understand we will echo characters so it
289          * should not do it locally. We don't tell the client to run linemode,
290          * because we want to handle line editing and tab completion and other
291          * stuff that requires char-by-char support. */
292         {
293                 static const char iacs_to_send[] ALIGN1 = {
294                         IAC, DO, TELOPT_ECHO,
295                         IAC, DO, TELOPT_NAWS,
296                         /* This requires telnetd.ctrlSQ.patch (incomplete) */
297                         /*IAC, DO, TELOPT_LFLOW,*/
298                         IAC, WILL, TELOPT_ECHO,
299                         IAC, WILL, TELOPT_SGA
300                 };
301                 /* This confuses iac_safe_write(), it will try to duplicate
302                  * each IAC... */
303                 //memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
304                 //ts->rdidx2 = sizeof(iacs_to_send);
305                 //ts->size2 = sizeof(iacs_to_send);
306                 /* So just stuff it into TCP stream! (no error check...) */
307 #if ENABLE_FEATURE_TELNETD_STANDALONE
308                 safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
309 #else
310                 safe_write(1, iacs_to_send, sizeof(iacs_to_send));
311 #endif
312                 /*ts->rdidx2 = 0; - xzalloc did it */
313                 /*ts->size2 = 0;*/
314         }
315
316         fflush_all();
317         pid = vfork(); /* NOMMU-friendly */
318         if (pid < 0) {
319                 free(ts);
320                 close(fd);
321                 /* sock will be closed by caller */
322                 bb_perror_msg("vfork");
323                 return NULL;
324         }
325         if (pid > 0) {
326                 /* Parent */
327                 ts->shell_pid = pid;
328                 return ts;
329         }
330
331         /* Child */
332         /* Careful - we are after vfork! */
333
334         /* Restore default signal handling ASAP */
335         bb_signals((1 << SIGCHLD) + (1 << SIGPIPE), SIG_DFL);
336
337         pid = getpid();
338
339         if (ENABLE_FEATURE_UTMP) {
340                 len_and_sockaddr *lsa = get_peer_lsa(sock);
341                 char *hostname = NULL;
342                 if (lsa) {
343                         hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
344                         free(lsa);
345                 }
346                 write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
347                 free(hostname);
348         }
349
350         /* Make new session and process group */
351         setsid();
352
353         /* Open the child's side of the tty */
354         /* NB: setsid() disconnects from any previous ctty's. Therefore
355          * we must open child's side of the tty AFTER setsid! */
356         close(0);
357         xopen(tty_name, O_RDWR); /* becomes our ctty */
358         xdup2(0, 1);
359         xdup2(0, 2);
360         tcsetpgrp(0, pid); /* switch this tty's process group to us */
361
362         /* The pseudo-terminal allocated to the client is configured to operate
363          * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
364         tcgetattr(0, &termbuf);
365         termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
366         termbuf.c_oflag |= ONLCR | XTABS;
367         termbuf.c_iflag |= ICRNL;
368         termbuf.c_iflag &= ~IXOFF;
369         /*termbuf.c_lflag &= ~ICANON;*/
370         tcsetattr_stdin_TCSANOW(&termbuf);
371
372         /* Uses FILE-based I/O to stdout, but does fflush_all(),
373          * so should be safe with vfork.
374          * I fear, though, that some users will have ridiculously big
375          * issue files, and they may block writing to fd 1,
376          * (parent is supposed to read it, but parent waits
377          * for vforked child to exec!) */
378         print_login_issue(G.issuefile, tty_name);
379
380         /* Exec shell / login / whatever */
381         login_argv[0] = G.loginpath;
382         login_argv[1] = NULL;
383         /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
384          * exec external program.
385          * NB: sock is either 0 or has CLOEXEC set on it.
386          * fd has CLOEXEC set on it too. These two fds will be closed here.
387          */
388         BB_EXECVP(G.loginpath, (char **)login_argv);
389         /* _exit is safer with vfork, and we shouldn't send message
390          * to remote clients anyway */
391         _exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
392 }
393
394 #if ENABLE_FEATURE_TELNETD_STANDALONE
395
396 static void
397 free_session(struct tsession *ts)
398 {
399         struct tsession *t;
400
401         if (option_mask32 & OPT_INETD)
402                 exit(EXIT_SUCCESS);
403
404         /* Unlink this telnet session from the session list */
405         t = G.sessions;
406         if (t == ts)
407                 G.sessions = ts->next;
408         else {
409                 while (t->next != ts)
410                         t = t->next;
411                 t->next = ts->next;
412         }
413
414 #if 0
415         /* It was said that "normal" telnetd just closes ptyfd,
416          * doesn't send SIGKILL. When we close ptyfd,
417          * kernel sends SIGHUP to processes having slave side opened. */
418         kill(ts->shell_pid, SIGKILL);
419         waitpid(ts->shell_pid, NULL, 0);
420 #endif
421         close(ts->ptyfd);
422         close(ts->sockfd_read);
423         /* We do not need to close(ts->sockfd_write), it's the same
424          * as sockfd_read unless we are in inetd mode. But in inetd mode
425          * we do not reach this */
426         free(ts);
427
428         /* Scan all sessions and find new maxfd */
429         G.maxfd = 0;
430         ts = G.sessions;
431         while (ts) {
432                 if (G.maxfd < ts->ptyfd)
433                         G.maxfd = ts->ptyfd;
434                 if (G.maxfd < ts->sockfd_read)
435                         G.maxfd = ts->sockfd_read;
436 #if 0
437                 /* Again, sockfd_write == sockfd_read here */
438                 if (G.maxfd < ts->sockfd_write)
439                         G.maxfd = ts->sockfd_write;
440 #endif
441                 ts = ts->next;
442         }
443 }
444
445 #else /* !FEATURE_TELNETD_STANDALONE */
446
447 /* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
448 #define free_session(ts) return 0
449
450 #endif
451
452 static void handle_sigchld(int sig UNUSED_PARAM)
453 {
454         pid_t pid;
455         struct tsession *ts;
456         int save_errno = errno;
457
458         /* Looping: more than one child may have exited */
459         while (1) {
460                 pid = wait_any_nohang(NULL);
461                 if (pid <= 0)
462                         break;
463                 ts = G.sessions;
464                 while (ts) {
465                         if (ts->shell_pid == pid) {
466                                 ts->shell_pid = -1;
467                                 update_utmp_DEAD_PROCESS(pid);
468                                 break;
469                         }
470                         ts = ts->next;
471                 }
472         }
473
474         errno = save_errno;
475 }
476
477 int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
478 int telnetd_main(int argc UNUSED_PARAM, char **argv)
479 {
480         fd_set rdfdset, wrfdset;
481         unsigned opt;
482         int count;
483         struct tsession *ts;
484 #if ENABLE_FEATURE_TELNETD_STANDALONE
485 #define IS_INETD (opt & OPT_INETD)
486         int master_fd = master_fd; /* for compiler */
487         int sec_linger = sec_linger;
488         char *opt_bindaddr = NULL;
489         char *opt_portnbr;
490 #else
491         enum {
492                 IS_INETD = 1,
493                 master_fd = -1,
494         };
495 #endif
496         INIT_G();
497
498         /* -w NUM, and implies -F. -w and -i don't mix */
499         IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
500         /* Even if !STANDALONE, we accept (and ignore) -i, thus people
501          * don't need to guess whether it's ok to pass -i to us */
502         opt = getopt32(argv, "f:l:Ki"
503                         IF_FEATURE_TELNETD_STANDALONE("p:b:F")
504                         IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
505                         &G.issuefile, &G.loginpath
506                         IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
507                         IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
508         );
509         if (!IS_INETD /*&& !re_execed*/) {
510                 /* inform that we start in standalone mode?
511                  * May be useful when people forget to give -i */
512                 /*bb_error_msg("listening for connections");*/
513                 if (!(opt & OPT_FOREGROUND)) {
514                         /* DAEMON_CHDIR_ROOT was giving inconsistent
515                          * behavior with/without -F, -i */
516                         bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
517                 }
518         }
519         /* Redirect log to syslog early, if needed */
520         if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
521                 openlog(applet_name, LOG_PID, LOG_DAEMON);
522                 logmode = LOGMODE_SYSLOG;
523         }
524 #if ENABLE_FEATURE_TELNETD_STANDALONE
525         if (IS_INETD) {
526                 G.sessions = make_new_session(0);
527                 if (!G.sessions) /* pty opening or vfork problem, exit */
528                         return 1; /* make_new_session printed error message */
529         } else {
530                 master_fd = 0;
531                 if (!(opt & OPT_WAIT)) {
532                         unsigned portnbr = 23;
533                         if (opt & OPT_PORT)
534                                 portnbr = xatou16(opt_portnbr);
535                         master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
536                         xlisten(master_fd, 1);
537                 }
538                 close_on_exec_on(master_fd);
539         }
540 #else
541         G.sessions = make_new_session();
542         if (!G.sessions) /* pty opening or vfork problem, exit */
543                 return 1; /* make_new_session printed error message */
544 #endif
545
546         /* We don't want to die if just one session is broken */
547         signal(SIGPIPE, SIG_IGN);
548
549         if (opt & OPT_WATCHCHILD)
550                 signal(SIGCHLD, handle_sigchld);
551         else /* prevent dead children from becoming zombies */
552                 signal(SIGCHLD, SIG_IGN);
553
554 /*
555    This is how the buffers are used. The arrows indicate data flow.
556
557    +-------+     wridx1++     +------+     rdidx1++     +----------+
558    |       | <--------------  | buf1 | <--------------  |          |
559    |       |     size1--      +------+     size1++      |          |
560    |  pty  |                                            |  socket  |
561    |       |     rdidx2++     +------+     wridx2++     |          |
562    |       |  --------------> | buf2 |  --------------> |          |
563    +-------+     size2++      +------+     size2--      +----------+
564
565    size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
566    size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
567
568    Each session has got two buffers. Buffers are circular. If sizeN == 0,
569    buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
570    rdidxN == wridxN.
571 */
572  again:
573         FD_ZERO(&rdfdset);
574         FD_ZERO(&wrfdset);
575
576         /* Select on the master socket, all telnet sockets and their
577          * ptys if there is room in their session buffers.
578          * NB: scalability problem: we recalculate entire bitmap
579          * before each select. Can be a problem with 500+ connections. */
580         ts = G.sessions;
581         while (ts) {
582                 struct tsession *next = ts->next; /* in case we free ts */
583                 if (ts->shell_pid == -1) {
584                         /* Child died and we detected that */
585                         free_session(ts);
586                 } else {
587                         if (ts->size1 > 0)       /* can write to pty */
588                                 FD_SET(ts->ptyfd, &wrfdset);
589                         if (ts->size1 < BUFSIZE) /* can read from socket */
590                                 FD_SET(ts->sockfd_read, &rdfdset);
591                         if (ts->size2 > 0)       /* can write to socket */
592                                 FD_SET(ts->sockfd_write, &wrfdset);
593                         if (ts->size2 < BUFSIZE) /* can read from pty */
594                                 FD_SET(ts->ptyfd, &rdfdset);
595                 }
596                 ts = next;
597         }
598         if (!IS_INETD) {
599                 FD_SET(master_fd, &rdfdset);
600                 /* This is needed because free_session() does not
601                  * take master_fd into account when it finds new
602                  * maxfd among remaining fd's */
603                 if (master_fd > G.maxfd)
604                         G.maxfd = master_fd;
605         }
606
607         {
608                 struct timeval *tv_ptr = NULL;
609 #if ENABLE_FEATURE_TELNETD_INETD_WAIT
610                 struct timeval tv;
611                 if ((opt & OPT_WAIT) && !G.sessions) {
612                         tv.tv_sec = sec_linger;
613                         tv.tv_usec = 0;
614                         tv_ptr = &tv;
615                 }
616 #endif
617                 count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
618         }
619         if (count == 0) /* "telnetd -w SEC" timed out */
620                 return 0;
621         if (count < 0)
622                 goto again; /* EINTR or ENOMEM */
623
624 #if ENABLE_FEATURE_TELNETD_STANDALONE
625         /* Check for and accept new sessions */
626         if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
627                 int fd;
628                 struct tsession *new_ts;
629
630                 fd = accept(master_fd, NULL, NULL);
631                 if (fd < 0)
632                         goto again;
633                 close_on_exec_on(fd);
634
635                 /* Create a new session and link it into active list */
636                 new_ts = make_new_session(fd);
637                 if (new_ts) {
638                         new_ts->next = G.sessions;
639                         G.sessions = new_ts;
640                 } else {
641                         close(fd);
642                 }
643         }
644 #endif
645
646         /* Then check for data tunneling */
647         ts = G.sessions;
648         while (ts) { /* For all sessions... */
649                 struct tsession *next = ts->next; /* in case we free ts */
650
651                 if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
652                         int num_totty;
653                         unsigned char *ptr;
654                         /* Write to pty from buffer 1 */
655                         ptr = remove_iacs(ts, &num_totty);
656                         count = safe_write(ts->ptyfd, ptr, num_totty);
657                         if (count < 0) {
658                                 if (errno == EAGAIN)
659                                         goto skip1;
660                                 goto kill_session;
661                         }
662                         ts->size1 -= count;
663                         ts->wridx1 += count;
664                         if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
665                                 ts->wridx1 = 0;
666                 }
667  skip1:
668                 if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
669                         /* Write to socket from buffer 2 */
670                         count = MIN(BUFSIZE - ts->wridx2, ts->size2);
671                         count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
672                         if (count < 0) {
673                                 if (errno == EAGAIN)
674                                         goto skip2;
675                                 goto kill_session;
676                         }
677                         ts->size2 -= count;
678                         ts->wridx2 += count;
679                         if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
680                                 ts->wridx2 = 0;
681                 }
682  skip2:
683                 /* Should not be needed, but... remove_iacs is actually buggy
684                  * (it cannot process iacs which wrap around buffer's end)!
685                  * Since properly fixing it requires writing bigger code,
686                  * we rely instead on this code making it virtually impossible
687                  * to have wrapped iac (people don't type at 2k/second).
688                  * It also allows for bigger reads in common case. */
689                 if (ts->size1 == 0) {
690                         ts->rdidx1 = 0;
691                         ts->wridx1 = 0;
692                 }
693                 if (ts->size2 == 0) {
694                         ts->rdidx2 = 0;
695                         ts->wridx2 = 0;
696                 }
697
698                 if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
699                         /* Read from socket to buffer 1 */
700                         count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
701                         count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
702                         if (count <= 0) {
703                                 if (count < 0 && errno == EAGAIN)
704                                         goto skip3;
705                                 goto kill_session;
706                         }
707                         /* Ignore trailing NUL if it is there */
708                         if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
709                                 --count;
710                         }
711                         ts->size1 += count;
712                         ts->rdidx1 += count;
713                         if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
714                                 ts->rdidx1 = 0;
715                 }
716  skip3:
717                 if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
718                         /* Read from pty to buffer 2 */
719                         count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
720                         count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
721                         if (count <= 0) {
722                                 if (count < 0 && errno == EAGAIN)
723                                         goto skip4;
724                                 goto kill_session;
725                         }
726                         ts->size2 += count;
727                         ts->rdidx2 += count;
728                         if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
729                                 ts->rdidx2 = 0;
730                 }
731  skip4:
732                 ts = next;
733                 continue;
734  kill_session:
735                 if (ts->shell_pid > 0)
736                         update_utmp_DEAD_PROCESS(ts->shell_pid);
737                 free_session(ts);
738                 ts = next;
739         }
740
741         goto again;
742 }