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