Chinaunix首页 | 论坛 | 博客
  • 博客访问: 359876
  • 博文数量: 105
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 826
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-16 13:58
个人简介

记录有意义的东西

文章分类

全部博文(105)

文章存档

2013年(105)

我的朋友

分类: LINUX

2013-05-01 16:25:29

  telnetd是一个telnet服务端程序

 

下载地址:

解压缩后进入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)  


 

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. }  


阅读(2763) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~