Chinaunix首页 | 论坛 | 博客
  • 博客访问: 342715
  • 博文数量: 166
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-21 17:29
文章分类

全部博文(166)

文章存档

2015年(60)

2014年(99)

2013年(7)

我的朋友

分类: LINUX

2014-05-13 16:05:49

原文地址:telnetd分析 作者:lwchsz

int
main(int argc, char *argv[]) 
{
    struct sockaddr_in from;
    int on = 1;
    socklen_t fromlen;
#ifndef REALLY_SMALL_TELNETD
    register int ch;
#endif
    
#if    defined(IPPROTO_IP) && defined(IP_TOS)
    int tos = -1;
#endif

    pfrontp = pbackp = ptyobuf;
    netip = netibuf;
    nfrontp = nbackp = netobuf;
#if    defined(ENCRYPT)
    nclearto = 0;
#endif

    progname = *argv;

#ifdef CRAY
    /*
     * Get number of pty's before trying to process options,
     * which may include changing pty range.
     */
    highpty = getnpty();
#endif /* CRAY */

#ifndef REALLY_SMALL_TELNETD //通过Inetd启动都不定义该宏,因为0,1,2已经是accept之后的套接字
    while ((ch = getopt(argc, argv, "d:a:e:lhnr:I:D:B:sS:a:X:L:")) != EOF) {
        switch(ch) {

#ifdef    AUTHENTICATE
        case 'a':
            /*
             * Check for required authentication level
             */
            if (strcmp(optarg, "debug") == 0) {
                extern int auth_debug_mode;
                auth_debug_mode = 1;
            } else if (strcasecmp(optarg, "none") == 0) {
                auth_level = 0;
            } else if (strcasecmp(optarg, "other") == 0) {
                auth_level = AUTH_OTHER;
            } else if (strcasecmp(optarg, "user") == 0) {
                auth_level = AUTH_USER;
            } else if (strcasecmp(optarg, "valid") == 0) {
                auth_level = AUTH_VALID;
            } else if (strcasecmp(optarg, "off") == 0) {
                /*
                 * This hack turns off authentication
                 */
                auth_level = -1;
            } else {
                fprintf(stderr,
                "telnetd: unknown authorization level for -a\n");
            }
            break;
#endif    /* AUTHENTICATE */

#ifdef BFTPDAEMON
        case 'B':
            bftpd++;
            break;
#endif /* BFTPDAEMON */

        case 'd':
            if (strcmp(optarg, "ebug") == 0) {
                debug++;
                break;
            }
            usage();
            /* NOTREACHED */
            break;

#ifdef DIAGNOSTICS
        case 'D':
            /*
             * Check for desired diagnostics capabilities.
             */
            if (!strcmp(optarg, "report")) {
                diagnostic |= TD_REPORT|TD_OPTIONS;
            } else if (!strcmp(optarg, "exercise")) {
                diagnostic |= TD_EXERCISE;
            } else if (!strcmp(optarg, "netdata")) {
                diagnostic |= TD_NETDATA;
            } else if (!strcmp(optarg, "ptydata")) {
                diagnostic |= TD_PTYDATA;
            } else if (!strcmp(optarg, "options")) {
                diagnostic |= TD_OPTIONS;
            } else {
                usage();
                /* NOT REACHED */
            }
            break;
#endif /* DIAGNOSTICS */

#ifdef    AUTHENTICATE
        case 'e':
            if (strcmp(optarg, "debug") == 0) {
                extern int auth_debug_mode;
                auth_debug_mode = 1;
                break;
            }
            usage();
            /* NOTREACHED */
            break;
#endif    /* AUTHENTICATE */

        case 'h':
            hostinfo = 0;
            break;

#if    defined(CRAY) && defined(NEWINIT)
        case 'I':
            {
            extern char *gen_id;
            gen_id = optarg;
            break;
            }
#endif    /* defined(CRAY) && defined(NEWINIT) */

#ifdef    LINEMODE
        case 'l':
            alwayslinemode = 1;
            break;
#endif    /* LINEMODE */

        case 'L':
            loginprg = optarg;
            break;

        case 'n':
            keepalive = 0;
            break;

#ifdef CRAY
        case 'r':
            {
            char *strchr();
            char *c;

            /*
             * Allow the specification of alterations
             * to the pty search range.  It is legal to
             * specify only one, and not change the
             * other from its default.
             */
            c = strchr(optarg, '-');
            if (c) {
                *c++ = '\0';
                highpty = atoi(c);
            }
            if (*optarg != '\0')
                lowpty = atoi(optarg);
            if ((lowpty > highpty) || (lowpty < 0) ||
                            (highpty > 32767)) {
                usage();
                /* NOT REACHED */
            }
            break;
            }
#endif    /* CRAY */

#ifdef    SecurID
        case 's':
            /* SecurID required */
            require_SecurID = 1;
            break;
#endif    /* SecurID */
        case 'S':
#ifdef    HAS_GETTOS
            if ((tos = parsetos(optarg, "tcp")) < 0)
                fprintf(stderr, "%s%s%s\n",
                    "telnetd: Bad TOS argument '", optarg,
                    "'; will try to use default TOS");
#else
            fprintf(stderr, "%s%s\n", "TOS option unavailable; ",
                        "-S flag not supported\n");
#endif
            break;

#ifdef    AUTHENTICATE
        case 'X':
            /*
             * Check for invalid authentication types
             */
            auth_disable_name(optarg);
            break;
#endif    /* AUTHENTICATE */

        default:
            fprintf(stderr, "telnetd: %c: unknown option\n", ch);
            /* FALLTHROUGH */
        case '?':
            usage();
            /* NOTREACHED */
        }
    }

    argc -= optind;
    argv += optind;
#endif

#ifndef REALLY_SMALL_TELNETD
    if (debug) {//一般都不是调试方式
        int s, ns;
        size_t foo;
        struct servent *sp;
        static struct sockaddr_in sn = { AF_INET };

        if (argc > 1) {
        usage();
        /* NOT REACHED */
        } else if (argc == 1) {
            if ((sp = getservbyname(*argv, "tcp"))!=NULL) {
            sn.sin_port = sp->s_port;
            }
            else {
            sn.sin_port = atoi(*argv);
            if ((int)sn.sin_port <= 0) {
                fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
                usage();
                /* NOT REACHED */
            }
            sn.sin_port = htons((u_short)sn.sin_port);
           }
        } else {
        sp = getservbyname("telnet", "tcp");
        if (sp == 0) {
            fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
            exit(1);
        }
        sn.sin_port = sp->s_port;
        }

        s = socket(AF_INET, SOCK_STREAM, 0);
        if (s < 0) {
            perror("telnetd: socket");;
            exit(1);
        }
        (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        if (bind(s, (struct sockaddr *)&sn, sizeof(sn)) < 0) {
        perror("bind");
        exit(1);
        }
        if (listen(s, 1) < 0) {
        perror("listen");
        exit(1);
        }
        foo = sizeof(sn);
        ns = accept(s, (struct sockaddr *)&sn, &foo);
        if (ns < 0) {
        perror("accept");
        exit(1);
        }
        (void) dup2(ns, 0);
        (void) close(ns);
        (void) close(s);
#ifdef convex
    } else if (argc == 1) {
        ; /* VOID*/        /* Just ignore the host/port name */
#endif
    } else if (argc > 0) {
        usage();
        /* NOT REACHED */
    }
#endif

#ifndef REALLY_SMALL_TELNETD
    openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
#endif
    fromlen = sizeof (from);
    if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {//获取对端地址
        fprintf(stderr, "%s: ", progname);
        perror("getpeername");
//        _exit(1);
    }
    if (keepalive &&
        setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
        /*syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");*/
    }

#if    defined(IPPROTO_IP) && defined(IP_TOS)
  {
# if    defined(HAS_GETTOS)
        struct tosent *tp;
        if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
            tos = tp->t_tos;
# endif
        if (tos < 0)
            tos = 020;    /* Low Delay bit */
        if (tos
           && (setsockopt(0, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0)
           && (errno != ENOPROTOOPT) )
            /*syslog(LOG_WARNING, "setsockopt (IP_TOS): %m")*/;
    }
#endif    /* defined(IPPROTO_IP) && defined(IP_TOS) */
    net = 0;//telnetd从此描述符读取网络输入
    doit(&from);
    /* NOTREACHED */
    return 0;
}

static void
doit(struct sockaddr_in *who)
{
    const char *host;
#ifndef REALLY_SMALL_TELNETD
    struct hostent *hp;
#endif
    int level;
    char user_name[256];

    /*
     * Find an available pty to use.
     */
#ifndef    convex
    pty = getpty();//打开一个tty,用于与login子进程通信
    if (pty < 0)
        fatal(net, "All network ports in use");
#else
    for (;;) {
        char *lp;
        extern char *line, *getpty();

        if ((lp = getpty()) == NULL)
            fatal(net, "Out of ptys");

        if ((pty = open(lp, 2)) >= 0) {
            strcpy(line,lp);
            line[5] = 't';
            break;
        }
    }
#endif

#ifndef REALLY_SMALL_TELNETD
    /* get name of connected client */
    hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
        who->sin_family);
    if (hp)
        host = hp->h_name;
    else
#endif
        host = inet_ntoa(who->sin_addr);

#ifndef REALLY_SMALL_TELNETD
    /*
     * We must make a copy because Kerberos is probably going
     * to also do a gethost* and overwrite the static data...
     */
    strncpy(remote_host_name, host, sizeof(remote_host_name)-1);
    remote_host_name[sizeof(remote_host_name)-1] = 0;
    host = remote_host_name;

    {
        struct hostent *h;
        gethostname(host_name, sizeof(host_name));
        h = gethostbyname(host_name);
        if (h) {
            strncpy(host_name, h->h_name, sizeof(host_name));
            host_name[sizeof(host_name)-1] = 0;
        }
    }
#endif

#if    defined(AUTHENTICATE) || defined(ENCRYPT)
    auth_encrypt_init(host_name, host, "TELNETD", 1);
#endif

    init_env();
    /*
     * get terminal type.
     */
    *user_name = 0;
    level = getterminaltype(user_name);
#ifdef ENV
    setenv("TERM", terminaltype ? terminaltype : "network", 1);
#endif

    /*
     * Start up the login process on the slave side of the terminal
     */
#ifndef    convex
    startslave(host, level, user_name);//启动login子进程

    telnet(net, pty);  /* begin server processing *///父进程进行处理,其实就是login子进程把它的输出重定向到pty,然后父进程从pty读取的送到net输出,父进程把用户的输入从net读入,然后写到pty,供login使用。这样久完成了用户输入用户名和密码的过程。
#else
    telnet(net, pty, host);
#endif
    /*NOTREACHED*/
}

void startslave(const char *host, int autologin, char *autoname)
{
    int i;
#ifdef    NEWINIT
    extern char *ptyip;
    struct init_request request;
    void nologinproc();
    int n;
#endif    /* NEWINIT */

#if defined(AUTHENTICATE)
    if (!autoname || !autoname[0]) autologin = 0;
    if (autologin < auth_level) {
    fatal(net, "Authorization failed");
    exit(1);
    }
#endif

#ifndef    NEWINIT

    if ((i = vfork())) {//创建子进程
        if (i<0)
        fatalperror(net, "fork");
    /* parent */
    signal(SIGHUP,SIG_IGN);
    } else {
    /* child */
    signal(SIGHUP,SIG_IGN);
    getptyslave();//子进程把pty复制到0,1,2,这样子进程的标准输入输出就是刚才的伪终端
    start_login(host, autologin, autoname);//让子进程执行login程序
    _exit(0);
    /*NOTREACHED*/
    }
#else    /* NEWINIT */

    /*
     * Init will start up login process if we ask nicely.  We only wait
     * for it to start up and begin normal telnet operation.
     */
    if ((i = open(INIT_FIFO, O_WRONLY)) < 0) {
    char tbuf[128];
    sprintf(tbuf, "Can't open %s\n", INIT_FIFO);
    fatalperror(net, tbuf);
    }
    memset(&request, 0, sizeof(request));
    request.magic = INIT_MAGIC;
    SCPYN(request.gen_id, gen_id);
    SCPYN(request.tty_id, &line[8]);
    SCPYN(request.host, host);
    SCPYN(request.term_type, terminaltype ? terminaltype : "network");
#if !defined(UNICOS5)
    request.signal = SIGCLD;
    request.pid = getpid();
#endif
#ifdef BFTPDAEMON
    /*
     * Are we working as the bftp daemon?
     */
    if (bftpd) {
    SCPYN(request.exec_name, BFTPPATH);
    }
#endif /* BFTPDAEMON */
    if (write(i, (char *)&request, sizeof(request)) < 0) {
    char tbuf[128];
    sprintf(tbuf, "Can't write to %s\n", INIT_FIFO);
    fatalperror(net, tbuf);
    }
    close(i);
    signal(SIGALRM, nologinproc);
    for (i = 0; ; i++) {
    char tbuf[128];
    alarm(15);
    n = read(pty, ptyip, BUFSIZ);
    if (i == 3 || n >= 0 || !gotalarm) break;
    gotalarm = 0;
    sprintf(tbuf, "telnetd: waiting for /etc/init to start login process on %s\r\n", line);
    write(net, tbuf, strlen(tbuf));
    }
    if (n < 0 && gotalarm) fatal(net, "/etc/init didn't start login process");
    pcc += n;
    alarm(0);
    signal(SIGALRM, SIG_DFL);
    return;
#endif    /* NEWINIT */
}

static int getptyslave(void)
{
#ifdef USE_OPENPTY
    struct winsize ws;
    int t = ptyslavefd;

        init_termbuf();

    if (def_row || def_col) {
        memset((char *)&ws, 0, sizeof(ws));
        ws.ws_col = def_col;
        ws.ws_row = def_row;
        ioctl(t, TIOCSWINSZ, (char *)&ws);
    }

    set_termbuf();

    tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
    tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);

    if (login_tty(t) == -1)
        fatalperror(net, "login_tty");
    if (net > 2)
        close(net);
    if (pty > 2)
        close(pty);

    return t;

#else /* ! USE_OPENPTY */

    register int t = -1;

#if !defined(CRAY) || !defined(NEWINIT)
# ifdef    LINEMODE
    int waslm;
# endif
# ifdef    TIOCGWINSZ
    struct winsize ws;
# endif
    /*
     * Opening the slave side may cause initilization of the
     * kernel tty structure.  We need remember the state of
     *     if linemode was turned on
     *    terminal window size
     *    terminal speed
     * so that we can re-set them if we need to.
     */
# ifdef    LINEMODE
    waslm = tty_linemode();
# endif


    /*
     * Make sure that we don't have a controlling tty, and
     * that we are the session (process group) leader.
     */
# ifdef    TIOCNOTTY
    t = open(_PATH_TTY, O_RDWR);
    if (t >= 0) {
    ioctl(t, TIOCNOTTY, (char *)0);
    close(t);
    }
# endif


# ifdef    CRAY
    /*
     * Wait for our parent to get the utmp stuff to get done.
     */
    utmp_sig_wait();
# endif

    t = cleanopen(line);
    if (t < 0) fatalperror(net, line);

    /*
     * set up the tty modes as we like them to be.
     */
    init_termbuf();
# ifdef    TIOCGWINSZ
    if (def_row || def_col) {
    bzero((char *)&ws, sizeof(ws));
    ws.ws_col = def_col;
    ws.ws_row = def_row;
    ioctl(t, TIOCSWINSZ, (char *)&ws);
    }
# endif

    /*
     * Settings for sgtty based systems
     */
# ifndef    USE_TERMIO
    termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS;
# endif    /* USE_TERMIO */

    /*
     * Settings for UNICOS
     */
# ifdef    CRAY
    termbuf.c_oflag = OPOST|ONLCR|TAB3;
    termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON;
    termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK;
    termbuf.c_cflag = EXTB|HUPCL|CS8;
# endif

    /*
     * Settings for all other termios/termio based
     * systems, other than 4.4BSD.  In 4.4BSD the
     * kernel does the initial terminal setup.
     */
# if defined(USE_TERMIO) && !defined(CRAY) && (BSD <= 43)
#  ifndef    OXTABS
#   define OXTABS    0
#  endif
    termbuf.c_lflag |= ECHO;
    termbuf.c_oflag |= ONLCR|OXTABS;
    termbuf.c_iflag |= ICRNL;
    termbuf.c_iflag &= ~IXOFF;
# endif /* defined(USE_TERMIO) && !defined(CRAY) && (BSD <= 43) */
    tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600);
    tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600);
# ifdef    LINEMODE
    if (waslm) tty_setlinemode(1);
# endif    /* LINEMODE */

    /*
     * Set the tty modes, and make this our controlling tty.
     */
    set_termbuf();
    if (login_tty(t) == -1) fatalperror(net, "login_tty");//调用login_tty复制描述符到0,1,2
#endif    /* !defined(CRAY) || !defined(NEWINIT) */
    if (net > 2) close(net);
    if (pty > 2) close(pty);
    return t;  /* ? was nothing here... */

#endif /* USE_OPENPTY */
}

int login_tty(int t) {
    if (setsid() < 0) fatalperror(net, "setsid()");
# ifdef    TIOCSCTTY
    if (ioctl(t, TIOCSCTTY, (char *)0) < 0) {
    fatalperror(net, "ioctl(sctty)");
    }
#  if defined(CRAY) && defined(SESS_CTTY)    /* SESS_CTTY is in param.h */
    /*
     * Close the hard fd to /dev/ttypXXX, and re-open through
     * the indirect /dev/tty interface.
     */
    close(t);
    if ((t = open("/dev/tty", O_RDWR)) < 0) {
    fatalperror(net, "open(/dev/tty)");
    }
#  endif
# else
    close(open(lyne, O_RDWR));
# endif
    if (t != 0) dup2(t, 0);
    if (t != 1) dup2(t, 1);
    if (t != 2) dup2(t, 2);
    if (t > 2) close(t);
    return 0;
}

#ifndef    convex
void telnet(int f, int p)
#else
void telnet(int f, int p, char *host)
#endif
{
    int on = 1;
#ifdef EDIT_HOST_STUFF
    char *HE;
    const char *IM;
#endif

    /*
     * Initialize the slc mapping table.
     */
    get_slc_defaults();

    /*
     * Do some tests where it is desireable to wait for a response.
     * Rather than doing them slowly, one at a time, do them all
     * at once.
     */
    if (my_state_is_wont(TELOPT_SGA))
    send_will(TELOPT_SGA, 1);
    /*
     * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
     * because 4.2 clients are unable to deal with TCP urgent data.
     *
     * To find out, we send out a "DO ECHO".  If the remote system
     * answers "WILL ECHO" it is probably a 4.2 client, and we note
     * that fact ("WILL ECHO" ==> that the client will echo what
     * WE, the server, sends it; it does NOT mean that the client will
     * echo the terminal input).
     */
    send_do(TELOPT_ECHO, 1);
   
#ifdef    LINEMODE
    if (his_state_is_wont(TELOPT_LINEMODE)) {
    /*
     * Query the peer for linemode support by trying to negotiate
     * the linemode option.
     */
    linemode = 0;
    editmode = 0;
    send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
    }
#endif    /* LINEMODE */

    /*
     * Send along a couple of other options that we wish to negotiate.
     */
    send_do(TELOPT_NAWS, 1);
    send_will(TELOPT_STATUS, 1);
    flowmode = 1;  /* default flow control state */
    send_do(TELOPT_LFLOW, 1);
   
    /*
     * Spin, waiting for a response from the DO ECHO.  However,
     * some REALLY DUMB telnets out there might not respond
     * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
     * telnets so far seem to respond with WONT for a DO that
     * they don't understand...) because by the time we get the
     * response, it will already have processed the DO ECHO.
     * Kludge upon kludge.
     */
    while (his_will_wont_is_changing(TELOPT_NAWS)) {
    ttloop();
    }
   
    /*
     * But...
     * The client might have sent a WILL NAWS as part of its
     * startup code; if so, we'll be here before we get the
     * response to the DO ECHO.  We'll make the assumption
     * that any implementation that understands about NAWS
     * is a modern enough implementation that it will respond
     * to our DO ECHO request; hence we'll do another spin
     * waiting for the ECHO option to settle down, which is
     * what we wanted to do in the first place...
     */
    if (his_want_state_is_will(TELOPT_ECHO) &&
    his_state_is_will(TELOPT_NAWS)) {
    while (his_will_wont_is_changing(TELOPT_ECHO))
        ttloop();
    }
    /*
     * On the off chance that the telnet client is broken and does not
     * respond to the DO ECHO we sent, (after all, we did send the
     * DO NAWS negotiation after the DO ECHO, and we won't get here
     * until a response to the DO NAWS comes back) simulate the
     * receipt of a will echo.  This will also send a WONT ECHO
     * to the client, since we assume that the client failed to
     * respond because it believes that it is already in DO ECHO
     * mode, which we do not want.
     */
    if (his_want_state_is_will(TELOPT_ECHO)) {
    DIAG(TD_OPTIONS,
         {sprintf(nfrontp, "td: simulating recv\r\n");
         nfrontp += strlen(nfrontp);});
    willoption(TELOPT_ECHO);
    }
   
    /*
     * Finally, to clean things up, we turn on our echo.  This
     * will break stupid 4.2 telnets out of local terminal echo.
     */
   
    if (my_state_is_wont(TELOPT_ECHO))
    send_will(TELOPT_ECHO, 1);
   
    /*
     * Turn on packet mode
     */
    ioctl(p, TIOCPKT, (char *)&on);
#if defined(LINEMODE) && defined(KLUDGELINEMODE)
    /*
     * Continuing line mode support.  If client does not support
     * real linemode, attempt to negotiate kludge linemode by sending
     * the do timing mark sequence.
     */
    if (lmodetype < REAL_LINEMODE)
    send_do(TELOPT_TM, 1);
#endif    /* defined(LINEMODE) && defined(KLUDGELINEMODE) */
   
    /*
     * Call telrcv() once to pick up anything received during
     * terminal type negotiation, 4.2/4.3 determination, and
     * linemode negotiation.
     */
    telrcv();
   
    ioctl(f, FIONBIO, (char *)&on);
    ioctl(p, FIONBIO, (char *)&on);
#if defined(CRAY2) && defined(UNICOS5)
    init_termdriver(f, p, interrupt, sendbrk);
#endif

#if defined(SO_OOBINLINE)
    setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
#endif    /* defined(SO_OOBINLINE) */
   
#ifdef    SIGTSTP
    signal(SIGTSTP, SIG_IGN);
#endif
#ifdef    SIGTTOU
    /*
     * Ignoring SIGTTOU keeps the kernel from blocking us
     * in ttioct() in /sys/tty.c.
     */
    signal(SIGTTOU, SIG_IGN);
#endif
   
    signal(SIGCHLD, cleanup);
   
#if defined(CRAY2) && defined(UNICOS5)
    /*
     * Cray-2 will send a signal when pty modes are changed by slave
     * side.  Set up signal handler now.
     */
    if ((int)signal(SIGUSR1, termstat) < 0)
    perror("signal");
    else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
    perror("ioctl:TCSIGME");
    /*
     * Make processing loop check terminal characteristics early on.
     */
    termstat();
#endif

#ifdef TIOCNOTTY
    {
    register int t;
    t = open(_PATH_TTY, O_RDWR);
    if (t >= 0) {
        (void) ioctl(t, TIOCNOTTY, (char *)0);
        (void) close(t);
    }
    }
#endif
   
#if defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY)
    setsid();
    ioctl(p, TIOCSCTTY, 0);
#endif

    /*
     * Show banner that getty never gave.
     *
     * We put the banner in the pty input buffer.  This way, it
     * gets carriage return null processing, etc., just like all
     * other pty --> client data.
     */
   
#if !defined(CRAY) || !defined(NEWINIT)
/*    if (getenv("USER"))
    hostinfo = 0;*/
#endif
   
#ifdef EDIT_HOST_STUFF
    IM = DEFAULT_IM;
    HE = 0;

    edithost(HE, host_name);
    if (hostinfo && *IM)
    putf(IM, ptyibuf2);
   
    if (pcc) strncat(ptyibuf2, ptyip, pcc+1);
#endif
   
    ptyip = ptyibuf2;
    pcc = strlen(ptyip);
#ifdef LINEMODE
    /*
     * Last check to make sure all our states are correct.
     */
    init_termbuf();
    localstat();
#endif    /* LINEMODE */

    DIAG(TD_REPORT,
     {sprintf(nfrontp, "td: Entering processing loop\r\n");
     nfrontp += strlen(nfrontp);});
   
#ifdef    convex
    startslave(host);
#endif

    for (;;) {
    fd_set ibits, obits, xbits;
    int c, hifd;
   
    if (ncc < 0 && pcc < 0)
        break;
   
#if    defined(CRAY2) && defined(UNICOS5)
    if (needtermstat) _termstat();
#endif    /* defined(CRAY2) && defined(UNICOS5) */
    FD_ZERO(&ibits);
    FD_ZERO(&obits);
    FD_ZERO(&xbits);
    hifd=0;
    /*
     * Never look for input if there's still
     * stuff in the corresponding output buffer
     */
    if (nfrontp - nbackp || pcc > 0) {
        FD_SET(f, &obits);
        if (f >= hifd) hifd = f+1;
    }
    else {
        FD_SET(p, &ibits);
        if (p >= hifd) hifd = p+1;
    }
    if (pfrontp - pbackp || ncc > 0) {
        FD_SET(p, &obits);
        if (p >= hifd) hifd = p+1;
    }
    else {
        FD_SET(f, &ibits);
        if (f >= hifd) hifd = f+1;
    }
    if (!SYNCHing) {
        FD_SET(f, &xbits);
        if (f >= hifd) hifd = f+1;
    }
    if ((c = select(hifd, &ibits, &obits, &xbits,
            (struct timeval *)0)) < 1) {//在net,pty上等待输入,中转login过程
        if (c == -1) {
        if (errno == EINTR) {
            continue;
        }
        }
        sleep(5);
        continue;
    }
   
    /*
     * Any urgent data?
     */
    if (FD_ISSET(net, &xbits)) {
        SYNCHing = 1;
    }
   
    /*
     * Something to read from the network...
     */
    if (FD_ISSET(net, &ibits)) {
#if !defined(SO_OOBINLINE)
        /*
         * In 4.2 (and 4.3 beta) systems, the
         * OOB indication and data handling in the kernel
         * is such that if two separate TCP Urgent requests
         * come in, one byte of TCP data will be overlaid.
         * This is fatal for Telnet, but we try to live
         * with it.
         *
         * In addition, in 4.2 (and...), a special protocol
         * is needed to pick up the TCP Urgent data in
         * the correct sequence.
         *
         * What we do is:  if we think we are in urgent
         * mode, we look to see if we are "at the mark".
         * If we are, we do an OOB receive.  If we run
         * this twice, we will do the OOB receive twice,
         * but the second will fail, since the second
         * time we were "at the mark", but there wasn't
         * any data there (the kernel doesn't reset
         * "at the mark" until we do a normal read).
         * Once we've read the OOB data, we go ahead
         * and do normal reads.
         *
         * There is also another problem, which is that
         * since the OOB byte we read doesn't put us
         * out of OOB state, and since that byte is most
         * likely the TELNET DM (data mark), we would
         * stay in the TELNET SYNCH (SYNCHing) state.
         * So, clocks to the rescue.  If we've "just"
         * received a DM, then we test for the
         * presence of OOB data when the receive OOB
         * fails (and AFTER we did the normal mode read
         * to clear "at the mark").
         */
        if (SYNCHing) {
        int atmark;
       
        ioctl(net, SIOCATMARK, (char *)&atmark);
        if (atmark) {
            ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
            if ((ncc == -1) && (errno == EINVAL)) {
            ncc = read(net, netibuf, sizeof (netibuf));
            if (sequenceIs(didnetreceive, gotDM)) {
                SYNCHing = stilloob(net);
            }
            }
        }
        else {
            ncc = read(net, netibuf, sizeof (netibuf));
        }
        }
        else {
        ncc = read(net, netibuf, sizeof (netibuf));
        }
        settimer(didnetreceive);
#else    /* !defined(SO_OOBINLINE)) */
        ncc = read(net, netibuf, sizeof (netibuf));
#endif    /* !defined(SO_OOBINLINE)) */
        if (ncc < 0 && errno == EWOULDBLOCK)
        ncc = 0;
        else {
        if (ncc <= 0) {
            break;
        }
        netip = netibuf;
        }
        DIAG((TD_REPORT | TD_NETDATA),
         {sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
         nfrontp += strlen(nfrontp);});
        DIAG(TD_NETDATA, printdata("nd", netip, ncc));
    }
   
    /*
     * Something to read from the pty...
     */
    if (FD_ISSET(p, &ibits)) {
        pcc = read(p, ptyibuf, BUFSIZ);
        /*
         * On some systems, if we try to read something
         * off the master side before the slave side is
         * opened, we get EIO.
         */
        if (pcc < 0 && (errno == EWOULDBLOCK || errno == EIO)) {
        pcc = 0;
        }
        else {
        if (pcc <= 0)
            break;
#if    !defined(CRAY2) || !defined(UNICOS5)
#ifdef    LINEMODE
                /*
                 * If ioctl from pty, pass it through net
                 */
        if (ptyibuf[0] & TIOCPKT_IOCTL) {
            copy_termbuf(ptyibuf+1, pcc-1);
            localstat();
            pcc = 1;
        }
#endif    /* LINEMODE */
        if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
            netclear();    /* clear buffer back */
#ifndef    NO_URGENT
            /*
             * There are client telnets on some
             * operating systems get screwed up
             * royally if we send them urgent
             * mode data.
             */
            *nfrontp++ = IAC;
            *nfrontp++ = DM;
            neturg = nfrontp-1; /* off by one XXX */
#endif
        }
        if (his_state_is_will(TELOPT_LFLOW) &&
            (ptyibuf[0] &
             (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
            (void) sprintf(nfrontp, "%c%c%c%c%c%c",
                   IAC, SB, TELOPT_LFLOW,
                   ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0,
                   IAC, SE);
            nfrontp += 6;
        }
        pcc--;
        ptyip = ptyibuf+1;
#else    /* defined(CRAY2) && defined(UNICOS5) */
        if (!uselinemode) {
            unpcc = pcc;
            unptyip = ptyibuf;
            pcc = term_output(&unptyip, ptyibuf2,
                      &unpcc, BUFSIZ);
            ptyip = ptyibuf2;
        }
        else ptyip = ptyibuf;
#endif    /* defined(CRAY2) && defined(UNICOS5) */
        }
    }
   
    while (pcc > 0) {
        if ((&netobuf[BUFSIZ] - nfrontp) < 2)
        break;
        c = *ptyip++ & 0377, pcc--;
        if (c == IAC)
        *nfrontp++ = c;
#if defined(CRAY2) && defined(UNICOS5)
        else if (c == '\n' &&
             my_state_is_wont(TELOPT_BINARY) && newmap)
        *nfrontp++ = '\r';
#endif    /* defined(CRAY2) && defined(UNICOS5) */
        *nfrontp++ = c;
        if ((c == '\r'  ) && (my_state_is_wont(TELOPT_BINARY))) {
        if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
            *nfrontp++ = *ptyip++ & 0377;
            pcc--;
        }
        else *nfrontp++ = '\0';
        }
    }
#if defined(CRAY2) && defined(UNICOS5)
    /*
     * If chars were left over from the terminal driver,
     * note their existence.
     */
    if (!uselinemode && unpcc) {
        pcc = unpcc;
        unpcc = 0;
        ptyip = unptyip;
    }
#endif    /* defined(CRAY2) && defined(UNICOS5) */

    if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
        netflush();
    if (ncc > 0)
        telrcv();
    if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
        ptyflush();
    }
    cleanup(0);
}
阅读(1466) | 评论(0) | 转发(0) |
0

上一篇:get_pid_by_name

下一篇:telnetd源码分析

给主人留下些什么吧!~~