telnetd是一个telnet服务端程序

    下载地址:http://www.busybox.net/
    解压缩后进入busybox目录
    make defconfig
    make
    make install
    然后会生成 _install 目录,里面就是编译好的可执行文件
    源码位于 ./networking/telnetd.c

    程序流程图:


    程序中非常重要的就是2个buf,位于struct tsession结构体之后

    1. 1. /*
    2. 2. This is how the buffers are used. The arrows indicate data flow.
    3. 3.
    4. 4. +-------+ wridx1++ +------+ rdidx1++ +----------+
    5. 5. | | <-------------- | buf1 | <-------------- | |
    6. 6. | | size1-- +------+ size1++ | |
    7. 7. | pty | | socket |
    8. 8. | | rdidx2++ +------+ wridx2++ | |
    9. 9. | | --------------> | buf2 | --------------> | |
    10. 10. +-------+ size2++ +------+ size2-- +----------+
    11. 11.
    12. 12. size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
    13. 13. size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
    14. 14.
    15. 15. Each session has got two buffers. Buffers are circular. If sizeN == 0,
    16. 16. buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
    17. 17. rdidxN == wridxN.
    18. 18. */

    socket接收到远端的数据,写入count个字节到buf1中从rdidx1开始的空闲区域,然后size1 += count;rdidx1 += count;
    pty可以写,从buf1中读取count个字节写入pty,然后size1 -= count;wridx1 += count;
    pty可以读,写入count个字节到buf2中从rdidx2开始的空闲区域,然后size2 += count;rdidx2 += count;
    socket可以发送数据到远端,从buf2中wridx2开始的位置读取count个字节,通过socket发送出去,然后size2 -= count;wridx2 += count;

    1. 1. master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
    2. 2. xlisten(master_fd, 1);
    3. 3.
    4. 4. FD_ZERO(&rdfdset);
    5. 5. FD_ZERO(&wrfdset);
    6. 6.
    7. 7. ts = G.sessions;
    8. 8. while (ts) {
    9. 9. struct tsession *next = ts->next; /* in case we free ts */
    10. 10. if (ts->shell_pid == -1) {
    11. 11. /* Child died and we detected that */
    12. 12. free_session(ts);
    13. 13. } else {
    14. 14. if (ts->size1 > 0) /* can write to pty */
    15. 15. FD_SET(ts->ptyfd, &wrfdset);
    16. 16. if (ts->size1 < BUFSIZE) /* can read from socket */
    17. 17. FD_SET(ts->sockfd_read, &rdfdset);
    18. 18. if (ts->size2 > 0) /* can write to socket */
    19. 19. FD_SET(ts->sockfd_write, &wrfdset);
    20. 20. if (ts->size2 < BUFSIZE) /* can read from pty */
    21. 21. FD_SET(ts->ptyfd, &rdfdset);
    22. 22. }
    23. 23. ts = next;
    24. 24. }
    25. 25.
    26. 26. FD_SET(master_fd, &rdfdset);
    27. 27. if (master_fd > G.maxfd)
    28. 28. G.maxfd = master_fd;
    29. 29.
    30. 30. count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
    31. 31.
    32. 32. if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
    33. 33. int fd;
    34. 34. struct tsession *new_ts;
    35. 35. fd = accept(master_fd, NULL, NULL);
    36. 36. new_ts = make_new_session(fd);
    37. 37. new_ts->next = G.sessions;
    38. 38. G.sessions = new_ts;
    39. 39. }
    40. 40.
    41. 41.
    42. 42.
    43. 43.
    44. 44. /* Then check for data tunneling */
    45. 45. ts = G.sessions;
    46. 46. while (ts) { /* For all sessions... */
    47. 47. struct tsession *next = ts->next; /* in case we free ts */
    48. 48.
    49. 49. if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
    50. 50. int num_totty;
    51. 51. unsigned char *ptr;
    52. 52. /* Write to pty from buffer 1 */
    53. 53. ptr = remove_iacs(ts, &num_totty);
    54. 54. count = safe_write(ts->ptyfd, ptr, num_totty);
    55. 55. if (count < 0) {
    56. 56. if (errno == EAGAIN)
    57. 57. goto skip1;
    58. 58. goto kill_session;
    59. 59. }
    60. 60. ts->size1 -= count;
    61. 61. ts->wridx1 += count;
    62. 62. if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
    63. 63. ts->wridx1 = 0;
    64. 64. }
    65. 65. skip1:
    66. 66. if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
    67. 67. /* Write to socket from buffer 2 */
    68. 68. count = MIN(BUFSIZE - ts->wridx2, ts->size2);
    69. 69. count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
    70. 70. if (count < 0) {
    71. 71. if (errno == EAGAIN)
    72. 72. goto skip2;
    73. 73. goto kill_session;
    74. 74. }
    75. 75. ts->size2 -= count;
    76. 76. ts->wridx2 += count;
    77. 77. if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
    78. 78. ts->wridx2 = 0;
    79. 79. }
    80. 80. skip2:
    81. 81. /* Should not be needed, but... remove_iacs is actually buggy
    82. 82. * (it cannot process iacs which wrap around buffer's end)!
    83. 83. * Since properly fixing it requires writing bigger code,
    84. 84. * we rely instead on this code making it virtually impossible
    85. 85. * to have wrapped iac (people don't type at 2k/second).
    86. 86. * It also allows for bigger reads in common case. */
    87. 87. if (ts->size1 == 0) {
    88. 88. ts->rdidx1 = 0;
    89. 89. ts->wridx1 = 0;
    90. 90. }
    91. 91. if (ts->size2 == 0) {
    92. 92. ts->rdidx2 = 0;
    93. 93. ts->wridx2 = 0;
    94. 94. }
    95. 95.
    96. 96. if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
    97. 97. /* Read from socket to buffer 1 */
    98. 98. count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
    99. 99. count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
    100. 100. if (count <= 0) {
    101. 101. if (count < 0 && errno == EAGAIN)
    102. 102. goto skip3;
    103. 103. goto kill_session;
    104. 104. }
    105. 105. /* Ignore trailing NUL if it is there */
    106. 106. if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
    107. 107. --count;
    108. 108. }
    109. 109. ts->size1 += count;
    110. 110. ts->rdidx1 += count;
    111. 111. if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
    112. 112. ts->rdidx1 = 0;
    113. 113. }
    114. 114. skip3:
    115. 115. if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
    116. 116. /* Read from pty to buffer 2 */
    117. 117. count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
    118. 118. count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
    119. 119. if (count <= 0) {
    120. 120. if (count < 0 && errno == EAGAIN)
    121. 121. goto skip4;
    122. 122. goto kill_session;
    123. 123. }
    124. 124. ts->size2 += count;
    125. 125. ts->rdidx2 += count;
    126. 126. if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
    127. 127. ts->rdidx2 = 0;
    128. 128. }
    129. 129. skip4:
    130. 130. ts = next;
    131. 131. continue;
    132. 132. kill_session:
    133. 133. if (ts->shell_pid > 0)
    134. 134. update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
    135. 135. free_session(ts);
    136. 136. ts = next;
    137. 137. }
    138. 138.
    139. 139. ---------------------------------
    140. 140. | | | | | | | | | | | | | | | | |
    141. 141. ---------------------------------
    142. 142. ^ ^
    143. 143. | |
    144. 144. ptr0 end
    145. 145.
    146. 146. remove_iacs函数对buf1中从wridx1开始size1长度的缓冲区进行操作,提取出实际有效地命令行语句,移动这段字符串到首部,
    147. 147. 更新wridx1 += ptr - totty;size1 -= ptr - totty;num_totty是实际有效地命令行语句的字节个数,返回首部地址。
    148. 148. static unsigned char *
    149. 149. remove_iacs(struct tsession *ts, int *pnum_totty)
    150. 150. {
    151. 151. unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
    152. 152. unsigned char *ptr = ptr0;
    153. 153. unsigned char *totty = ptr;
    154. 154. unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
    155. 155. int num_totty;
    156. 156.
    157. 157. while (ptr < end) {
    158. 158.
    159. 159. /* 字符串处理 */
    160. 160. }
    161. 161.
    162. 162. num_totty = totty - ptr0;
    163. 163. *pnum_totty = num_totty;
    164. 164. /* The difference between ptr and totty is number of iacs
    165. 165. we removed from the stream. Adjust buf1 accordingly */
    166. 166. if ((ptr - totty) == 0) /* 99.999% of cases */
    167. 167. return ptr0;
    168. 168. ts->wridx1 += ptr - totty;
    169. 169. ts->size1 -= ptr - totty;
    170. 170. /* Move chars meant for the terminal towards the end of the buffer */
    171. 171. return memmove(ptr - num_totty, ptr0, num_totty);
    172. 172. }
    173. 173.
    174. 174.
    175. 175.
    176. 176. free_session函数从G.sessions链表头中删除ts指向的结构,关闭ptysocket文件句柄,释放内存,更新G.maxfd
    177. 177. static void
    178. 178. free_session(struct tsession *ts)
    179. 179.


    make_new_session函数非常关键,它调用xgetpty打开一个伪终端,调用vfork创建一个子进程,父进程保存打开的伪终端和相关句柄
    后返回,子进程调用setsid,关闭标准输入,打开伪终端,然后将0重定向到标准输出和标准错误,然后执行/bin/login,login执行
    验证过程后启动shell程序。
    以后只要父进程往获得的伪终端句柄里面写数据,就是把输入写到子进程启动的shell里面,shell执行之后,父进程通过read读取伪
    终端句柄,就可以读取到shell的标准输出。

    1. static struct tsession *
    2. make_new_session
    3. {
    4. struct tsession *ts = xzalloc
    5. fd = xgetpty(tty_name);
    6.  ts->ptyfd = fd;
    7.  pid =vfork();
    8. if(pid > 0)
    9.  {
    10. //父进程
    11. return ts;   
    12.  }
    13. 
    14. //子进程
    15.  setsid();                  //设置SID
    16.  close(0);                  //关闭标准输入
    17.  xopen(tty_name, O_RDWR);   //打开伪终端,注意这个时候默认返回的是数字号最小的句柄,也就是0
    18.  dup2(0,1);                 //将伪终端句柄重定向到标准输出
    19.  dup2(0,2);                 //将伪终端句柄重定向到标准错误
    20.  execvp("/bin/login",);     //执行/bin/login
    21.  _exit(EXIT_FAILURE);       //之后直接退出
    22. }