首頁>Club>
12
回覆列表
  • 1 # dcz西北狼

    下面的debug資訊來自與/tmp/telenet.debug檔案的一部分。

    是客戶端按下字母e後發生的四個階段。這篇主要分析第一階段和第二階段。

    也就是telrcv函式的主要的功能。

    td: netread 1 chars

    nd: 65 e

    td: ptyflush 1 chars

    pd: 65 e

    td: ptyread 2 chars

    pd: 0065 .e

    td: netflush 1 chars

    下面的是telnetd.c裡面最主要的一個函式,其中裡面的for迴圈也是理解telnetd的工作機制最主要的部分。

    int

    telnetd_run (void)

    {

    ...

    for (;;)

    {

    fd_set ibits, obits, xbits;

    register int c;

    if (net_input_level () < 0 && pty_input_level () < 0)

    break;

    FD_ZERO (&ibits);

    FD_ZERO (&obits);

    FD_ZERO (&xbits);

    /* Never look for input if there"s still stuff in the corresponding

    output buffer */

    if (net_output_level () || pty_input_level () > 0)

    FD_SET (net, &obits);

    else

    FD_SET (pty, &ibits);

    if (pty_output_level () || net_input_level () > 0)

    FD_SET (pty, &obits);

    else

    FD_SET (net, &ibits);

    if (!SYNCHing)

    FD_SET (net, &xbits);

    if ((c = select (nfd, &ibits, &obits, &xbits, NULL)) <= 0)

    {

    if (c == -1 && errno == EINTR)

    continue;

    sleep (5);

    continue;

    }

    if (FD_ISSET (net, &xbits))

    SYNCHing = 1;

    if (FD_ISSET (net, &ibits))

    {

    /* Something to read from the network... */

    /*FIXME: handle !defined(SO_OOBINLINE) */

    net_read (); 這裡是第一階段執行的函式

    }

    if (FD_ISSET (pty, &ibits))

    {

    /* Something to read from the pty... */

    if (pty_read () <= 0)

    break;

    /* The first byte is now TIOCPKT data. Peek at it. */

    c = pty_get_char (1);

    #if defined TIOCPKT_IOCTL

    if (c & TIOCPKT_IOCTL)

    {

    pty_get_char (0);

    copy_termbuf (); /* Pty buffer is now emptied. */

    localstat ();

    }

    #endif

    if (c & TIOCPKT_FLUSHWRITE)

    {

    static char flushdata[] = { IAC, DM };

    pty_get_char (0);

    netclear (); /* clear buffer back */

    net_output_datalen (flushdata, sizeof (flushdata));

    set_neturg ();

    DEBUG (debug_options, 1, printoption ("td: send IAC", DM));

    }

    if (his_state_is_will (TELOPT_LFLOW)

    && (c & (TIOCPKT_NOSTOP | TIOCPKT_DOSTOP)))

    {

    int newflow = (c & TIOCPKT_DOSTOP) ? 1 : 0;

    if (newflow != flowmode)

    {

    net_output_data ("%c%c%c%c%c%c",

    IAC, SB, TELOPT_LFLOW,

    flowmode ? LFLOW_ON : LFLOW_OFF, IAC, SE);

    }

    }

    pty_get_char (0); /* Discard the TIOCPKT preamble. */

    }

    while (pty_input_level () > 0)

    {

    if (net_buffer_is_full ())

    break;

    c = pty_get_char (0);

    if (c == IAC)

    net_output_byte (c);

    net_output_byte (c);

    if (c == "\r" && my_state_is_wont (TELOPT_BINARY))

    {

    if (pty_input_level () > 0 && pty_get_char (1) == "\n")

    net_output_byte (pty_get_char (0));

    else

    net_output_byte (0);

    }

    }

    if (FD_ISSET (net, &obits) && net_output_level () > 0)

    netflush ();

    if (net_input_level () > 0)

    telrcv ();

    if (FD_ISSET (pty, &obits) && pty_output_level () > 0)

    ptyflush (); 這裡是第二階段執行的函式。

    /* Attending to the child must come last in the loop,

    * so as to let pending data be flushed, mainly to the

    * benefit of the remote and expecting client.

    */

    if (pending_sigchld) {

    /* Check for pending output, independently of OBITS. */

    if (net_output_level () > 0)

    netflush ();

    cleanup (SIGCHLD); /* Not returning from this. */

    }

    }

    net_read函式分析。這個函式是接收來自net的一個字元。

    ncc是個數,用到的netibuf,網路輸入緩衝區。可以這麼理解。

    netip是網路輸入緩衝區的指標。

    int

    net_read (void)

    {

    ncc = read (net, netibuf, sizeof (netibuf));

    if (ncc < 0 && errno == EWOULDBLOCK)

    ncc = 0;

    else if (ncc == 0)

    {

    syslog (LOG_INFO, "telnetd: peer died");

    cleanup (0);

    /* NOT REACHED */

    }

    else if (ncc > 0)

    {

    netip = netibuf;

    DEBUG (debug_report, 1,

    debug_output_data ("td: netread %d chars\r\n", ncc));

    DEBUG (debug_net_data, 1, printdata ("nd", netip, ncc));

    }

    return ncc;

    }

    telrcv函式是一個關鍵的函式,在檔案state.c中定義。

    和telnet協議狀態機有關。

    比如如果第一個位元組是FF也就是IAC,那麼下面的位元組是命令位元組。命令選項位元組。

    net_get_char函式和pty_output_byte函式是理解telrcv函式的主要的地方。

    其他的語句都和狀態機有關。這兩個函式是取一個字元,函式放到pty緩衝區裡。

    void

    telrcv (void)

    {

    register int c;

    static int state = TS_DATA;

    while ((net_input_level () > 0) & !pty_buffer_is_full ())

    {

    c = net_get_char (0);

    #ifdef ENCRYPTION

    if (decrypt_input)

    c = (*decrypt_input) (c);

    #endif /* ENCRYPTION */

    switch (state)

    {

    case TS_CR:

    state = TS_DATA;

    /* Strip off \n or \0 after a \r */

    if ((c == 0) || (c == "\n"))

    break;

    /* FALL THROUGH */

    case TS_DATA:

    if (c == IAC)

    {

    state = TS_IAC;

    break;

    }

    /*

    * We now map \r\n ==> \r for pragmatic reasons.

    * Many client implementations send \r\n when

    * the user hits the CarriageReturn key.

    *

    * We USED to map \r\n ==> \n, since \r\n says

    * that we want to be in column 1 of the next

    * printable line, and \n is the standard

    * unix way of saying that (\r is only good

    * if CRMOD is set, which it normally is).

    */

    if ((c == "\r") && his_state_is_wont (TELOPT_BINARY))

    {

    int nc = net_get_char (1);

    #ifdef ENCRYPTION

    if (decrypt_input)

    nc = (*decrypt_input) (nc & 0xff);

    #endif /* ENCRYPTION */

    /*

    * If we are operating in linemode,

    * convert to local end-of-line.

    */

    if (linemode

    && net_input_level () > 0

    && (("\n" == nc) || (!nc && tty_iscrnl ())))

    {

    net_get_char (0); /* Remove from the buffer */

    c = "\n";

    }

    else

    {

    #ifdef ENCRYPTION

    if (decrypt_input)

    (*decrypt_input) (-1);

    #endif /* ENCRYPTION */

    state = TS_CR;

    }

    }

    pty_output_byte (c);

    break;

    case TS_IAC:

    gotiac:

    switch (c)

    {

    /*

    * Send the process on the pty side an

    * interrupt. Do this with a NULL or

    * interrupt char; depending on the tty mode.

    */

    case IP:

    DEBUG (debug_options, 1, printoption ("td: recv IAC", c));

    send_intr ();

    break;

    case BREAK:

    DEBUG (debug_options, 1, printoption ("td: recv IAC", c));

    send_brk ();

    break;

    int

    net_get_char (int peek)

    {

    if (peek)

    return *netip;

    else if (ncc > 0)

    {

    ncc--;

    return *netip++ & 0377;

    }

    return 0;

    }

    void

    pty_output_byte (int c)

    {

    *pfrontp++ = c;

    }

    這裡是第二階段相關的函式。比較好理解。

    主要的功能是把緩衝區的字元放到/dev/pty裡面

    a b c d e f g

    | |

    pbackp pfrontp

    上面的pfrontp指標指向的是字母g,如果再輸入一個字元h,那麼pfrontp指標就指向字元h。

    上面的pbackp指標指向的是字母b,如果要拿出一個字元,那麼應該先拿出字元b,然後是c,再然後是d。

    void

    ptyflush (void)

    {

    int n;

    if ((n = pfrontp - pbackp) > 0)

    {

    DEBUG (debug_report, 1,

    debug_output_data ("td: ptyflush %d chars\r\n", n));

    DEBUG (debug_pty_data, 1, printdata ("pd", pbackp, n));

    syslog (LOG_NOTICE, "ptyflush pbackp = %s", pbackp);

    n = write (pty, pbackp, n);

    }

    if (n < 0)

    {

    if (errno == EWOULDBLOCK || errno == EINTR)

    return;

    cleanup (0);

    /* NOT REACHED */

    }

    pbackp += n;

    if (pbackp == pfrontp)

    pbackp = pfrontp = ptyobuf;

    }

  • 中秋節和大豐收的關聯?
  • 家裡老人呆在家裡無聊有什麼方法幫助她?沒有文化,不認識字,不喜歡廣場舞活動。