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