telnetd是一个telnet服务端程序

下载地址:

解压缩后进入busybox目录

make defconfig

make

make install

然后会生成 _install 目录,里面就是编译好的可执行文件

源码位于 ./networking/telnetd.c

程序流程图:

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

/*

This is how the buffers are used. The arrows indicate data flow.

+-------+     wridx1++     +------+     rdidx1++     +----------+

|       |

|       |     size1--      +------+     size1++      |          |

|  pty  |                                            |  socket  |

|       |     rdidx2++     +------+     wridx2++     |          |

|       |  --------------> | buf2 |  --------------> |          |

+-------+     size2++      +------+     size2--      +----------+

size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"

size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"

Each session has got two buffers. Buffers are circular. If sizeN == 0,

buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases

rdidxN == wridxN.

*/

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;

master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);

xlisten(master_fd, 1);

FD_ZERO(&rdfdset);

FD_ZERO(&wrfdset);

ts = G.sessions;

while(ts) {

structtsession *next = ts->next;/* in case we free ts */

if(ts->shell_pid == -1) {

/* Child died and we detected that */

free_session(ts);

} else{

if(ts->size1 > 0)/* can write to pty */

FD_SET(ts->ptyfd, &wrfdset);

if(ts->size1 /* can read from socket */

FD_SET(ts->sockfd_read, &rdfdset);

if(ts->size2 > 0)/* can write to socket */

FD_SET(ts->sockfd_write, &wrfdset);

if(ts->size2 /* can read from pty */

FD_SET(ts->ptyfd, &rdfdset);

}

ts = next;

}

FD_SET(master_fd, &rdfdset);

if(master_fd > G.maxfd)

G.maxfd = master_fd;

count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);

if(!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {

intfd;

structtsession *new_ts;

fd = accept(master_fd, NULL, NULL);

new_ts = make_new_session(fd);

new_ts->next = G.sessions;

G.sessions = new_ts;

}

/* Then check for data tunneling */

ts = G.sessions;

while(ts) {/* For all sessions... */

structtsession *next = ts->next;/* in case we free ts */

if(/*ts->size1 &&*/FD_ISSET(ts->ptyfd, &wrfdset)) {

intnum_totty;

unsigned char*ptr;

/* Write to pty from buffer 1 */

ptr = remove_iacs(ts, &num_totty);

count = safe_write(ts->ptyfd, ptr, num_totty);

if(count

if(errno == EAGAIN)

gotoskip1;

gotokill_session;

}

ts->size1 -= count;

ts->wridx1 += count;

if(ts->wridx1 >= BUFSIZE)/* actually == BUFSIZE */

ts->wridx1 = 0;

}

skip1:

if(/*ts->size2 &&*/FD_ISSET(ts->sockfd_write, &wrfdset)) {

/* Write to socket from buffer 2 */

count = MIN(BUFSIZE - ts->wridx2, ts->size2);

count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);

if(count

if(errno == EAGAIN)

gotoskip2;

gotokill_session;

}

ts->size2 -= count;

ts->wridx2 += count;

if(ts->wridx2 >= BUFSIZE)/* actually == BUFSIZE */

ts->wridx2 = 0;

}

skip2:

/* Should not be needed, but... remove_iacs is actually buggy

* (it cannot process iacs which wrap around buffer's end)!

* Since properly fixing it requires writing bigger code,

* we rely instead on this code making it virtually impossible

* to have wrapped iac (people don't type at 2k/second).

* It also allows for bigger reads in common case. */

if(ts->size1 == 0) {

ts->rdidx1 = 0;

ts->wridx1 = 0;

}

if(ts->size2 == 0) {

ts->rdidx2 = 0;

ts->wridx2 = 0;

}

if(/*ts->size1 FD_ISSET(ts->sockfd_read, &rdfdset)) {

/* Read from socket to buffer 1 */

count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);

count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);

if(count <= 0) {

if(count

gotoskip3;

gotokill_session;

}

/* Ignore trailing NUL if it is there */

if(!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {

--count;

}

ts->size1 += count;

ts->rdidx1 += count;

if(ts->rdidx1 >= BUFSIZE)/* actually == BUFSIZE */

ts->rdidx1 = 0;

}

skip3:

if(/*ts->size2 FD_ISSET(ts->ptyfd, &rdfdset)) {

/* Read from pty to buffer 2 */

count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);

count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);

if(count <= 0) {

if(count

gotoskip4;

gotokill_session;

}

ts->size2 += count;

ts->rdidx2 += count;

if(ts->rdidx2 >= BUFSIZE)/* actually == BUFSIZE */

ts->rdidx2 = 0;

}

skip4:

ts = next;

continue;

kill_session:

if(ts->shell_pid > 0)

update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/NULL,/*username:*/NULL,/*hostname:*/NULL);

free_session(ts);

ts = next;

}

---------------------------------

| | | | | | | | | | | | | | | | |

---------------------------------

^                ^

|                |

ptr0             end

remove_iacs函数对buf1中从wridx1开始size1长度的缓冲区进行操作,提取出实际有效地命令行语句,移动这段字符串到首部,

更新wridx1 += ptr - totty;size1 -= ptr - totty;num_totty是实际有效地命令行语句的字节个数,返回首部地址。

staticunsignedchar*

remove_iacs(structtsession *ts,int*pnum_totty)

{

unsigned char*ptr0 = TS_BUF1(ts) + ts->wridx1;

unsigned char*ptr = ptr0;

unsigned char*totty = ptr;

unsigned char*end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);

intnum_totty;

while(ptr

/* 字符串处理 */

}

num_totty = totty - ptr0;

*pnum_totty = num_totty;

/* The difference between ptr and totty is number of iacs

we removed from the stream. Adjust buf1 accordingly */

if((ptr - totty) == 0)/* 99.999% of cases */

returnptr0;

ts->wridx1 += ptr - totty;

ts->size1 -= ptr - totty;

/* Move chars meant for the terminal towards the end of the buffer */

returnmemmove(ptr - num_totty, ptr0, num_totty);

}

free_session函数从G.sessions链表头中删除ts指向的结构,关闭pty和socket文件句柄,释放内存,更新G.maxfd

staticvoid

free_session(structtsession *ts)

make_new_session函数非常关键,它调用xgetpty打开一个伪终端,调用vfork创建一个子进程,父进程保存打开的伪终端和相关句柄

后返回,子进程调用setsid,关闭标准输入,打开伪终端,然后将0重定向到标准输出和标准错误,然后执行/bin/login,login执行

验证过程后启动shell程序。

以后只要父进程往获得的伪终端句柄里面写数据,就是把输入写到子进程启动的shell里面,shell执行之后,父进程通过read读取伪

终端句柄,就可以读取到shell的标准输出。

staticstructtsession *

make_new_session

{

structtsession *ts = xzalloc

fd = xgetpty(tty_name);

ts->ptyfd = fd;

pid =vfork();

if(pid > 0)

{

//父进程

returnts;

}

//子进程

setsid();                  //设置SID

close(0);                  //关闭标准输入

xopen(tty_name, O_RDWR);   //打开伪终端,注意这个时候默认返回的是数字号最小的句柄,也就是0

dup2(0,1);                 //将伪终端句柄重定向到标准输出

dup2(0,2);                 //将伪终端句柄重定向到标准错误

execvp("/bin/login",);//执行/bin/login

_exit(EXIT_FAILURE);       //之后直接退出

}

linux telnetd 源代码,telnetd源码分析相关推荐

  1. linux显示启动logo源码分析以及修改显示logo

    1.linux显示启动logo整个流程分析 (1)logo图片在内核源码中是以ppm格式的文件保存,在编译内核时会把ppm格式的文件自动转换成.c文件,在c文件中会构造一个struct linux_l ...

  2. Linux brk(),mmap()系统调用源码分析3:brk()的内存申请流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存申请流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  3. linux nDPI 协议检测 源码分析

    关于nDPI的基本功能就不在这介绍了,有兴趣了解的读者可以阅读官方的快速入门指南:https://github.com/ntop/nDPI/blob/dev/doc/nDPI_QuickStartGu ...

  4. Linux系统编程 / triggerhappy 源码分析(3.select 的应用)

    哈喽,我是老吴,继续记录我的学习心得. 一.进步的滞后性 我们期望进步是线性: 每一个人付出一些努力后,都希望它有立竿见影的效果. 现实是: 做出努力后,结果的显现往往滞后. 只有在几个月或几年后,我 ...

  5. linux wifi 源代码,Linux无线认证----wifidog源码分析

    wifidog wifidog开源模块,通过iptable对报文进行重定向到端口2060接口,对报文进行拦截,利用iptable实现用户上网行为管理功能,目前市面上的无线多采用此模块进行portale ...

  6. linux之虚拟文件系统源码分析(详解)

    文章目录 前言 基础知识 VFS的数据结构 正篇 前言 ​ 虚拟文件系统是一个很庞大的架构,如果要分析的面面俱到,会显得特别复杂而笨拙,让人看着看着,就不知所云了(当然主要还是笔者太菜),所以这篇博客 ...

  7. linux gnu binutils,binutils源码分析之准备篇

    1.什么是binutils 先写一个简单的hello_world.c程序. #include int main() { printf("Hello World!\n"); retu ...

  8. Linux 4.x MTD源码分析-cfi-flash设备probe过程分析

    1. MTD chip driver模块的注册 在MTD子系统中,不管你是什么类型的存储芯片,RAM也好,ROM也好,CFI接口flash或者JEDEC接口的flash,他们的driver都是以mtd ...

  9. ARMv8 Linux内核head.S源码分析

    ARMv8Linux内核head.S主要工作内容: 1. 从el2特权级退回到el1 2. 确认处理器类型 3. 计算内核镜像的起始物理地址及物理地址与虚拟地址之间的偏移 4. 验证设备树的地址是否有 ...

  10. linux smp 引导多核源码分析

    1 SMP cpu map和操作函数集初始化 1.1 初步初始化cpu map start_kernel ---------->smp_setup_processor_id linux 从第一个 ...

最新文章

  1. 如何在SAP Kyma的控制台里扩展新的UI
  2. 熬夜肝完这份Framework笔记,已拿到offer
  3. python如何爬虫股票数据_自学python之爬虫3股票数据爬虫
  4. Netscreen204防火墙配置网络设备的SNMP及Syslog配置
  5. 俄罗斯被指为 SolarWinds 供应链事件元凶,技术公司受制裁,常用5大漏洞遭曝光...
  6. indesign打开黑屏 mac_看完这篇文章,90%的电脑黑屏问题都可以解决了!
  7. html快闪软件制作,抖音最强快闪ppt怎么做?快闪PPT快闪制作方法介绍
  8. Navicat for MySQL 安装教程
  9. android 串口调试工具,串口调试助手下载-串口调试助手下载v1.0.4 安卓版-西西软件下载...
  10. CDH通过parcels安装组件,激活时卡住,取消激活
  11. 论文投稿指南——中文核心期刊推荐(环境科学 2)
  12. 波司登少了“鹅胸前的四片毛”
  13. 【欺骗眼睛】可能你不会相信,图中的A色块和B色块是同一个颜色
  14. 关于小程序开发的一些资源
  15. 记者成互联网公司老板,他把猪八戒网做到市值百亿
  16. 3D格式转换工具HOOPS Exchange最全技术指南(二):4大功能特征与典型使用场景
  17. 计算机二级选择题不单独计时,考试倒计时:如何备考计算机二级office?
  18. vue Eharts 中国地图,包含台湾省,涟漪效果,流向图
  19. Python 竟能开发如此精美的俄罗斯方块!
  20. 百度网盘官方推出的“下载提速”方法 — 免会员

热门文章

  1. java编程 问题_Java编程常见问题汇总
  2. IOS网络第一天 - 02SDWebImage
  3. 什么是接口的幂等性,如何实现接口幂等性?一文搞定
  4. Halcon捕捉错误的方式(tray catch)
  5. vue组件间通信的13种方式
  6. 【架构师入门——什么是组件化】
  7. Linux下从GitHub下载单个文件或者目录
  8. python使用代理访问服务器
  9. 计算机苏教版初一教案,苏教版初中七年级信息技术全册教案.docx
  10. python如何实现飞机上下移动_python实现飞船游戏的纵向移动