source: branches/3.2/mindi-busybox/networking/telnetd.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 6 years ago
  • Update mindi-busybox to 1.21.1
File size: 21.0 KB
Line 
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}
Note: See TracBrowser for help on using the repository browser.