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