miniftp项目总结(四)

被动模式的列表显示
查看linux中进程的命令

ps -ef | grep miniftpd

结束进程

killall miniftpd
static void do_port(session_t *sess)
{
	unsigned char v[6] = {0};
	sscanf(sess->arg, "%d, %d, %d, %d, %d, %d,", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]);
	//printf("v[0] = %d, v[1]=%d, v[2]=%d, v[3]=%d, v[4]=%d, v[5]=%d\n",
	//	v[0],v[1],v[2],v[3],v[4],v[5]);
	sess->port_addr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
	memset(sess->port_addr, 0, sizeof(struct sockaddr_in));
	//sin_family
	sess->port_addr->sin_family = AF_INET;
	//sin_addr
	unsigned char *p = (unsigned char*)&sess->port_addr->sin_addr;
	p[0] = v[0];
	p[1] = v[1];
	p[2] = v[2];
	p[3] = v[3];
	//sin_port
	p = (unsigned char*)&sess->port_addr->sin_port;
	p[0] = v[4];
	p[1] = v[5];


	//200 PORT command successful. Consider using PASV.
	ftp_reply(sess, FTP_PORTOK, "PORT command successful. Consider using PASV.");
}


static void do_pasv(session_t *sess)
{
	//ip = "192.168.0.200"
	char *ip = "192.168.0.200";
	int sockfd = tcp_server(ip, 0);

	struct sockaddr_in address;
	socklen_t addrlen = sizeof(address);
	if(getsockname(sockfd, (struct sockaddr*)&address, &addrlen) < 0)
		ERR_EXIT("getsockname");//获取该套接字所绑定的端口

	//printf("ip = %s\n", inet_ntoa(addr.sin_addr));
	//printf("port = %d\n",ntohs(addr.sin_port));
	unsigned short port = ntohs(address.sin_port);
	unsigned char addr[6] = {0};
	sscanf(ip, "%u.%u.%u.%u", &addr[0], &addr[1], &addr[2], &addr[3]);
	addr[4] = ((port>>8) & 0x00ff);
	addr[5] = port & 0x00ff;

	char buf[MAX_BUFFER_SIZE] = {0};
	sprintf(buf, "Entering Passive Mode (%u,%u,%u,%u,%u,%u)", addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]);
	//port = 8080
	//227 Entering Passive Mode (192,168,0,200,76,240).
	sess->pasv_listen_fd = sockfd;//数据套接字
	ftp_reply(sess, FTP_PASVOK, buf);
}

///
int port_active(session_t *sess)
{
	if(sess->port_addr != 0)
		return 1;
	return 0;
}
int pasv_active(session_t *sess)
{
	if(sess->pasv_listen_fd != -1)
		return 1;
	return 0;
}
int get_transfer_fd(session_t *sess)
{
	if(!port_active(sess) && !pasv_active(sess))
	{
		//425 Use PORT or PASV first
		ftp_reply(sess, FTP_BADSENDCONN, "Use PORT or PASV first");
		return 0;
	}


	if(port_active(sess) && pasv_active(sess))
	{
		//425 PORT both PASV active.
		ftp_reply(sess, FTP_BADSENDCONN, "PORT both PASV active.");
		return 0;
	}


	int datafd;
	if(port_active(sess))
	{

		if((datafd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
			return 0;
		socklen_t addrlen = sizeof(struct sockaddr);

		/*
		struct sockaddr_in address;
		address.sin_family = AF_INET;
		address.sin_port = htons(20);
		address.sin_addr.s_addr = inet_addr("192.168.0.200");

		if(bind(datafd, (struct sockaddr*)&address, addrlen) < 0)
			ERR_EXIT("bind 20");
			*/

		if(connect(datafd, (struct sockaddr*)sess->port_addr, addrlen) < 0)
			return 0;

	}

	if(pasv_active(sess))
	{
		struct sockaddr_in addrcli;
		socklen_t addrlen = sizeof(addrcli);
		if((datafd = accept(sess->pasv_listen_fd, (struct sockaddr*)&addrcli, &addrlen))<0)
			return 0;
		close(sess->pasv_listen_fd);//数据连接建立完成,就关闭
		sess->pasv_listen_fd = -1;//还原原来的初始值
	}
	sess->data_fd = datafd;

	if(sess->port_addr)
	{
		free(sess->port_addr);
		sess->port_addr = 0;
	}

	return 1;
}

进程模型修改
ftp进程和nobody进程。在两个进程之间创建socketpair作为通道,nobody循环readline()。当ftp进程结束之后,readline()返回值为0,随后nobody进程结束。最后在主线中安装忽略子进程结束的信号,使主线进程忽略nobody进程,避免nobody进程变成僵尸进程。
进程间的通信
ftp进程<-------->nobody进程之间的通信
首先定义一个内部通信模块privsocket.h,用来发送和接收:命令、字符串、整数、套接字等

#ifndef _PRIV_SOCK_H_
#define _PRIV_SOCK_H_

#include"session.h" 

//FTP服务进程向nobody进程请求的命令
#define PRIV_SOCK_GET_DATA_SOCK   1
#define PRIV_SOCK_PASV_ACTIVE     2
#define PRIV_SOCK_PASV_LISTEN     3
#define PRIV_SOCK_PASV_ACCEPT     4

//nobody 进程对FTP服务进程的应答
#define PRIV_SOCK_RESULT_OK      1
#define PRIV_SOCK_RESULT_BAD     2

void priv_sock_init(session_t *sess);
void priv_sock_close(session_t *sess);
void priv_sock_set_parent_context(session_t *sess);
void priv_sock_set_child_context(session_t *sess);
void priv_sock_send_cmd(int fd, char cmd);
char priv_sock_get_cmd(int fd);
void priv_sock_send_result(int fd, char res);
char priv_sock_get_result(int fd);
void priv_sock_send_int(int fd, int the_int);
int  priv_sock_get_int(int fd);
void priv_sock_send_buf(int fd, const char *buf, unsigned int len);
void priv_sock_recv_buf(int fd, char *buf, unsigned int len);
void priv_sock_send_fd(int sock_fd, int fd);
int  priv_sock_recv_fd(int sock_fd);



#endif /* _PRIV_SOCK_H_ */

nobody进程协助主动模式建立数据连接

下面简述一下过程:

  1. ftp进程向nobody进程发送命令请求获取数据套接字。
  2. nobody进程收到请求,并执行相应的函数:创建流式套接字datafd,并设置端口重用,绑定20端口,阻塞。
  3. ftp进程向nobody进程发送解析到的cli的IP和PORT,阻塞。
  4. nobody进程收到IP和PORT,建立连接,向ftp进程发送PRIV_SOCK_RESULT_OK,而后发送datafd,关闭datafd。
  5. ftp进程将收到的datafd存入sess->data_fd。

nobody进程权限提升

查看内核版本

uname -a 

为什么要用nobody进程?
主动模式时候需要绑定20端口,只有用nobody进程提升权限,才能够实现绑定
主动模式为什么一定要绑定20端口?
提升nobody进程权限的方法:

//最小化特权提升函数
void minimize_privilege(void)
{
	struct passwd *pw = getpwnam("nobody");
	if(pw == NULL)
		return;
	if(setegid(pw->pw_gid) < 0)
		ERR_EXIT("setegid");
	if(seteuid(pw->pw_uid) < 0)
		ERR_EXIT("seteuid");

	struct __user_cap_header_struct cap_header;
	struct __user_cap_data_struct   cap_data;
	//初始化
	memset(&cap_header, 0, sizeof(cap_header));
	memset(&cap_data,   0, sizeof(cap_data));
	//根据手册赋相应的值
	cap_header.version = _LINUX_CAPABILITY_VERSION_1;//根据系统的版本填充
	cap_header.pid = 0;//提升为root权限

	__u32 cap_mask = 0;//设置具体权限的掩码
	cap_mask |= (1<<CAP_NET_BIND_SERVICE);//使其具有绑定小于1024端口的权限
	cap_data.effective = cap_data.permitted = cap_mask;
	cap_data.inheritable = 0;//????

	capset(&cap_header, &cap_data);
}

nobody进程协助被动模式建立数据连接
被动模式的实现要复杂很多!封装和不封装各有自己的好处。

  1. ftp进程发送命令,向nobody进程请求监听套接字,阻塞。
  2. nobody进程创建套接字,获取临时端口。并保存socketfd到sess->pasv_listen_fd。向ftp进程发送port。
  3. ftp进程收到port,并将IP和PORT发送到cli。
  4. 判断pasv是否被激活?向nobody 进程发送请求命令;nobody进程执行相应的函数,判断session结构体中的pasv_listen_fd是否更新,更新的话,应答1(说明pasv激活)。
  5. ftp进程向nobody进程发送命令,请求accept套接字,阻塞
  6. nobody进程,aceept (pasv_listen_fd),应答OK,并将accept得到的datafd,发送给ftp进程,close datafd , close pasv_listen_fd。
  7. ftp进程将收到的datafd存入会话结构体中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值