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