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