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