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