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