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