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