miniftp (简化版vsftp) 项目开发及源码

1.miniftp

1.1项目简介

本次项目是一简化版的vsftpd
我们经常会需要从linux 到windows平台进行文件传递,或者上传本地文件到服务器,再从服务器下载,本次项目主要就是开发一个服务器,用于接收客户端发送的文件存到服务器中。
能够与多个客户端根据网络ip/TCP协议建立连接正常通信,实现进程间通信,建立服务器内部进程服务模型,能够读取目录文件信息并且发送等

1.2ftp了解

FTP就是文件传输协议。用于互联网双向传输,控制文件下载空间在服务器复制文件从本地计算机或本

地上传文件复制到服务器上的空间。
下面给出了用(Windows)sleapftp 与 (Centos 7)vsftpd的命令交互

[00:19:21] 正在连接到 192.168.2.106,端口 21 (#1)
[00:19:21] 已连接到服务器,正在等待响应...
[00:19:21] 220 (vsFTPd 3.0.2)
[00:19:21] USER liu
[00:19:21] 331 Please specify the password.
[00:19:21] PASS (隐藏)
[00:19:21] 230 Login successful.
[00:19:21] SYST
[00:19:21] 215 UNIX Type: L8
[00:19:21] FEAT
[00:19:21] 211-Features:
[00:19:21]  EPRT
[00:19:21]  EPSV
[00:19:21]  MDTM
[00:19:21]  PASV
[00:19:21]  REST STREAM
[00:19:21]  SIZE
[00:19:21]  TVFS
[00:19:21]  UTF8
[00:19:21] 211 End
[00:19:21] CLNT LeapFTP 3.1.0.50
[00:19:21] 500 Unknown command.
[00:19:21] PWD
[00:19:21] 257 "/home/liu"
[00:19:21] TYPE A
[00:19:21] 200 Switching to ASCII mode.
[00:19:21] PORT 192,168,2,101,57,33
[00:19:21] 200 PORT command successful. Consider using PASV.
[00:19:21] LIST
[00:19:21] 425 Failed to establish connection.
[00:19:21] PASV
[00:19:21] 227 Entering Passive Mode (192,168,2,106,33,111).
[00:19:21] LIST
[00:19:21] 150 Here comes the directory listing.
[00:19:21] 226 Directory send OK.
[00:19:21] 传输: [643 字节 | 0.001| 627.930 KB/s]
[00:20:10] NOOP
[00:20:10] 200 NOOP ok.
[00:20:24] QUIT
[00:20:24] 221 Goodbye.
[00:20:24] 已从服务器断开(在线: 00:01:03)

这个过程可以简单概括为客户端发送命令然后服务端对发送的命令进行解析并响应。这里做一个简单了解

1.3开发环境搭建

虚拟机搭建

环境搭建不必说,windows linux 两个系统是必须有的 这里我使用vm ware建立的centos 7虚拟机这里就不多介绍了,xshell xftp 这些基础软件也是必备的

vsftpd的安装

1.linux 环境下发送命令yum install vsftpd -y
yum如果不能用一般都是yum环境配置。或者虚拟机挂载出现问题
2.vsftpd的配置
目录在vim /etc/vsftpd/vsftpd.conf下 基本保持默认配置就够使用,具体配置可以根据个人需要
我们这次的目标就是模拟vsftpd写一个自己的ftp软件因此这里下载这个软件并学会使用是很关键的。
3.3 vsftpd的启动、停止
systemctl start vsftpd | systemctl stop vsftpd
3.4 vsftpd的防火墙关闭
使用客户端连接ftp,有时会因为防火墙的缘故而导致连接不上,因此一般关闭防火墙.防火墙必须关掉奥!
systemctl stop firewalld
3.5 Linux下的lftp演示(可忽略)
可以在linux 输入下列命令 下载lftp 即一个ftp协议的客户端
yum install lftp -y
lftp 192.168.232.10
3.6 leapftp(必须下载)
没有写客户端因此需要借助leapftp来访问我们的服务器

1.4项目技术概况

本次项目核心难点在于,项目体系的架构,进程间的服务控制关系,对linux 用户,进程号 ,i\o,文件信息的访问。

2.功能模块代码

公共头文件

common.h

#ifndef _COMMON_H_
#define _COMMON_H_
#define MAX_BUFFER_SIZE 1024
#define MAX_COMMAND_LINE 1024
#define MAX_COMMAND 32
#define MAX_ARG  1024

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "ftpcods.h"
#include<fcntl.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/stat.h>
#include <pwd.h>
#include<shadow.h>

#include <time.h>
#include <crypt.h>

#include <dirent.h>

#include<linux/capability.h>
#include<sys/syscall.h>

#define ERR_EXIT(m) \
	do{\
	perror(m);\
	exit(EXIT_FAILURE);\
	}while(0)
#endif

宏参数定义头文件

ftpcods.h

#ifndef _FTP_CODES_H_
#define _FTP_CODES_H_

#define FTP_DATACONN 150
#define FTP_NOOPOK 200
#define FTP_TYPEOK 200
#define FTP_PORTOK 200
#define FTP_EPRTOK 200
#define FTP_UMASKOK 200
#define FTP_CHMODOK 200
#define FTP_EPSVALLOK 200
#define FTP_STRUOK 200
#define FTP_MODEOK 200
#define FTP_PBSZOK 200
#define FTP_PROTOK 200
#define FTP_OPTSOK 200
#define FTP_ALLOOK 202
#define FTP_FEAT 211
#define FTP_STATOK 211
#define FTP_SIZEOK 213
#define FTP_MDTMOK 213
#define FTP_STATFILE_OK 213
#define FTP_SITEHELP 214
#define FTP_HELP 214
#define FTP_SYSTOK 215
#define FTP_GREET 220
#define FTP_GOODBYE 221
#define FTP_ABOR_NOCONN 225
#define FTP_TRANSFEROK 226
#define FTP_ABOROK 226
#define FTP_PASVOK 227
#define FTP_EPSVOK 229
#define FTP_LOGINOK 230
#define FTP_AUTHOK 234
#define FTP_CWDOK 250
#define FTP_RMDIROK 250
#define FTP_DELEOK 250
#define FTP_RENAMEOK 250
#define FTP_PWDOK 257
#define FTP_MKDIROK 257
#define FTP_GIVEPWORD 331
#define FTP_RESTOK 350
#define FTP_RNFROK 350
#define FTP_IDLE_TIMEOUT 421
#define FTP_DATA_TIMEOUT 421
#define FTP_TOO_MANY_USERS 421
#define FTP_IP_LIMIT 421
#define FTP_IP_DENY 421
#define FTP_TLS_FAIL 421
#define FTP_BADSENDCONN 425
#define FTP_BADSENDNET 426
#define FTP_BADSENDFILE 451
#define FTP_BADCMD 500
#define FTP_BADOPTS 501
#define FTP_COMMANDNOTIMPL 502
#define FTP_NEEDUSER 503
#define FTP_NEEDRNFR 503
#define FTP_BADPBSZ 503
#define FTP_BADPROT 503
#define FTP_BADSTRU 504
#define FTP_BADMODE 504
#define FTP_BADAUTH 504
#define FTP_NOSUCHPROT 504
#define FTP_NEEDENCRYPT 522
#define FTP_EPSVBAD 522
#define FTP_DATATLSBAD 522
#define FTP_LOGINERR 530
#define FTP_NOHANDLEPROT 536
#define FTP_FILEFAIL 550
#define FTP_NOPERM 550
#define FTP_UPLOADFAIL 553


#endif /* _FTP_CODES_H_ */

项目整体架构主进程(main入口主进程)

miniftp.c

#include "common.h"
#include "sysutil.h"
#include "session.h"

int main(int argc , char *argv[])
{
	if(getuid()!=0)//id root uid 是0
	{
		printf("bit miniftp:must be started as root.\n");
		exit(EXIT_FAILURE);
	}
	session_t sess = 
	{
		/* 控制连接 */
		-1,-1,"", "", "",
		/* 数据连接 */
		NULL, -1, -1,
		/* ftp 协议状态 */
		0, NULL,
		/* 父子进程通道 */
		-1, -1,
	};

	int listenfd = tcp_server ("192.168.2.106",9188);

	int sockConn;
	struct sockaddr_in addrCli;
	socklen_t addrlen;

	while(1)
	{
		if((sockConn = accept(listenfd ,(struct sockaddr*)&addrCli,&addrlen))<0)
			ERR_EXIT("accept");

		pid_t pid = fork();
		if(pid == -1)
			ERR_EXIT("fork");
		if(pid == 0)
		{
			
			close(listenfd);
			printf("//wang sesiion\n");
			
			sess.ctrl_fd = sockConn;
			begin_session(&sess);
			exit(EXIT_SUCCESS);
		}
		else
		{
			close(sockConn);
		}
	
	}
	close(listenfd);
	return 0;
}

套接字模块

sysutil.h

#ifndef _SYSUTIL_H_
#define _SYSUTIL_H_
#include "common.h"
int tcp_server (const char * host , unsigned short port);
int tcp_client();

const char* statbuf_get_perms(struct stat *sbuf);
const char* statbuf_get_date(struct stat *sbuf);
void send_fd(int sock_fd, int fd);
int recv_fd(const int sock_fd);
#endif     

sysutil.c

#include "sysutil.h"


int tcp_server (const char * host , unsigned short port)
{
	int listenfd;

	if((listenfd = socket (AF_INET,SOCK_STREAM,0)) < 0)
		ERR_EXIT("tcp_server");
	
	struct sockaddr_in addrSer;
	addrSer.sin_family = AF_INET;
	addrSer.sin_port = htons(port);
	addrSer.sin_addr.s_addr = inet_addr(host);
	int on =1;
	if(setsockopt(listenfd , SOL_SOCKET,SO_REUSEADDR , &on ,sizeof(on))<0)
		ERR_EXIT("setsockopt");

	if(bind(listenfd,(struct sockaddr*)&addrSer ,sizeof(addrSer))<0)
	ERR_EXIT("bind");

	if(listen(listenfd,SOMAXCONN)<0)
		ERR_EXIT("listen");
	return listenfd;
}

int tcp_client(int port)
{
	int sock;
	if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		ERR_EXIT("tcp_client");

	if(port > 0)
	{
		int on = 1;
		if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
			ERR_EXIT("setsockopt");

		struct sockaddr_in address;
		address.sin_family = AF_INET;
		address.sin_addr.s_addr = INADDR_ANY;
		address.sin_port = htons(port);
		if(bind(sock, (struct sockaddr*)&address, sizeof(struct sockaddr)) < 0)
			ERR_EXIT("bind 20");
	}

	return sock;
}
const char* statbuf_get_perms(struct stat *sbuf)
{
	//- --- --- ---
	static char perms[] = "----------";
	mode_t mode = sbuf->st_mode;
	switch(mode & S_IFMT)
	{
	case S_IFSOCK:
		perms[0] = 's';
		break;
	case S_IFLNK:
		perms[0] = 'l';
		break;
	case S_IFREG:
		perms[0] = '-';
		break;
	case S_IFBLK:
		perms[0] = 'b';
		break;
	case S_IFDIR:
		perms[0] = 'd';
		break;
	case S_IFCHR:
		perms[0] = 'c';
		break;
	case S_IFIFO:
		perms[0] = 'p';
		break;
	}

	if(mode & S_IRUSR)
		perms[1] = 'r';
	if(mode & S_IWUSR)
		perms[2] = 'w';
	if(mode & S_IXUSR)
		perms[3] = 'x';

	if(mode & S_IRGRP)
		perms[4] = 'r';
	if(mode & S_IWGRP)
		perms[5] = 'w';
	if(mode & S_IXGRP)
		perms[6] = 'x';

	if(mode & S_IROTH)
		perms[7] = 'r';
	if(mode & S_IWOTH)
		perms[8] = 'w';
	if(mode & S_IXOTH)
		perms[9] = 'x';

	return perms;
}

const char* statbuf_get_date(struct stat *sbuf)
{
	static char datebuf[64] = {0};
	time_t file_time = sbuf->st_mtime;
	struct tm *ptm = localtime(&file_time);
	strftime(datebuf, 64, "%b %e %H:%M",  ptm);
	return datebuf;
}
void send_fd(int sock_fd, int fd)
{
	int ret;
	struct msghdr msg;
	struct cmsghdr *p_cmsg;
	struct iovec vec;
	char cmsgbuf[CMSG_SPACE(sizeof(fd))];
	int *p_fds;
	char sendchar = 0;
	msg.msg_control = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);
	p_cmsg = CMSG_FIRSTHDR(&msg);
	p_cmsg->cmsg_level = SOL_SOCKET;
	p_cmsg->cmsg_type = SCM_RIGHTS;
	p_cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
	p_fds = (int*)CMSG_DATA(p_cmsg);
	*p_fds = fd;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &vec;
	msg.msg_iovlen = 1;
	msg.msg_flags = 0;

	vec.iov_base = &sendchar;
	vec.iov_len = sizeof(sendchar);
	ret = sendmsg(sock_fd, &msg, 0);
	if (ret != 1)
		ERR_EXIT("sendmsg");
}

int recv_fd(const int sock_fd)
{
	int ret;
	struct msghdr msg;
	char recvchar;
	struct iovec vec;
	int recv_fd;
	char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
	struct cmsghdr *p_cmsg;
	int *p_fd;
	vec.iov_base = &recvchar;
	vec.iov_len = sizeof(recvchar);
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_iov = &vec;
	msg.msg_iovlen = 1;
	msg.msg_control = cmsgbuf;
	msg.msg_controllen = sizeof(cmsgbuf);
	msg.msg_flags = 0;

	p_fd = (int*)CMSG_DATA(CMSG_FIRSTHDR(&msg));
	*p_fd = -1;  
	ret = recvmsg(sock_fd, &msg, 0);
	if (ret != 1)
		ERR_EXIT("recvmsg");

	p_cmsg = CMSG_FIRSTHDR(&msg);
	if (p_cmsg == NULL)
		ERR_EXIT("no passed fd");


	p_fd = (int*)CMSG_DATA(p_cmsg);
	recv_fd = *p_fd;
	if (recv_fd == -1)
		ERR_EXIT("no passed fd");

	return recv_fd;
}

字符串处理

str.h

#ifndef _STR_H_
#define _STR_H_
#include "common.h"

void str_trim_crlf(char *str);
void str_split(const char *str ,char *left ,char *right ,char c);
#endif

str.c

#include "str.h"

void str_trim_crlf(char *str)//ȥ\r\n
{
	assert(str != NULL);
	char *p =str + (strlen (str)-1);
	while(*p=='\n'||*p=='\r')
	{
		*p-- ='\0';
	}
} 
void str_split(const char *str ,char *left ,char *right ,char c)
{
	assert(str !=NULL);
	char *pos =strchr(str,c);
	if(pos == NULL)
		strcpy(left,str);
	else
	{
		strncpy(left,str,pos-str);
		strcpy(right,pos+1);
	}
}

会话层(nobody进程与其服务子进程的建立)

session.h

#ifndef _SESSION_H_
#define _SESSION_H_
#include "common.h"

typedef struct session
{
	//控制链接
	uid_t uid;
	int ctrl_fd;
	char cmdline[MAX_COMMAND_LINE];
	char cmd[MAX_COMMAND];
	char arg[MAX_ARG];
	/* 父子进程通道 */
	
//数据链接
	struct sockaddr_in *port_addr;//对方主动 发送的地址端口号信息 用这个指针存储
	int  data_fd;

	int  pasv_listen_fd;//对方被动 创建服务器 

	
	///*FTP协议状态*/
    int	is_ascii;
	char *rnfr_name;
    ///*FTP父子通信进程*/

	long long restart_pos;
	

	int parent_fd;
	int child_fd;



}session_t;

void begin_session(session_t *sess);
#endif

session.c

#include "session.h"
    
void begin_session(session_t *sess)
{
	priv_sock_init(sess);

	pid_t pid =fork();
	if(pid == -1)
		ERR_EXIT("fork");//
	if(pid == 0)
	{
	   priv_sock_set_child_context(sess);
		handle_child(sess);
	}
	else
	{
		
	 priv_sock_set_parent_context(sess);
		handle_parent(sess);
	}
}  

nobody进程

privparent.h

#ifndef _PRIVPARENT_H_
#define _PRIVPARENT_H_
#include "common.h"
#include "session.h"

void handle_parent(session_t *sess);
#endif 

privparent.c

#include"privparent.h"
#include"privsock.h"
#include"session.h"

//获取主动模式数据连接套接字
static void privop_pasv_get_data_sock(session_t *sess); 

//判断是否处于被动模式的激活状态
static void privop_pasv_active(session_t *sess); 

//获取被动模式下的监听端口
static void privop_pasv_listen(session_t *sess);

//获取被动模式下的数据连接套接字
static void privop_pasv_accept(session_t *sess); 

//提升权限用capset函数提升到到root绑定20端口
static void minimize_privilege()
{
	//更改进程名为nobody
	struct passwd *pw = getpwnam("nobody");
	if(pw == NULL)
		ERR_EXIT("getpwname");
	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_2;//版本64位
	cap_header.pid = 0;//用户id号 root 

	unsigned int cap_mask = 0;
	cap_mask |= (1 << CAP_NET_BIND_SERVICE);  //0000 0000 0000 0000 0001 0000 0000 0000 绑定一个特权端口的能力

	cap_data.effective = cap_data.permitted = cap_mask;//允许的权限
	cap_data.inheritable = 0; // 不继承之前的权限信息,一次机会用完就没
	
	capset(&cap_header, &cap_data);//用于设置权限能力
}

//nobody 进程
void handle_parent(session_t *sess)
{
	
	minimize_privilege();

	char cmd;
	while(1)
	{
		//不停的等待ftp进程的命令
		cmd = priv_sock_get_cmd(sess->parent_fd);
		switch(cmd)
		{
		case PRIV_SOCK_GET_DATA_SOCK:
			privop_pasv_get_data_sock(sess);
			break;
		case PRIV_SOCK_PASV_ACTIVE:
			privop_pasv_active(sess);
			break;
		case PRIV_SOCK_PASV_LISTEN:
			privop_pasv_listen(sess);
			break;
		case PRIV_SOCK_PASV_ACCEPT:
			privop_pasv_accept(sess);
			break;
		}
	}
}

static void privop_pasv_get_data_sock(session_t *sess)
{
	unsigned short port = (unsigned short)priv_sock_get_int(sess->parent_fd);
	char ip[16] = {0};
	priv_sock_recv_buf(sess->parent_fd, ip, sizeof(ip));

	struct sockaddr_in address;
	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	address.sin_addr.s_addr = inet_addr(ip);

	int fd = tcp_client(20);  //绑定20端口
	if(fd == -1)
	{
		priv_sock_send_result(sess->parent_fd, PRIV_SOCK_RESULT_BAD);
		return;
	}
	if(connect(fd, (struct sockaddr*)&address, sizeof(address)) < 0)
	{
		close(fd);
		priv_sock_send_result(sess->parent_fd, PRIV_SOCK_RESULT_BAD);
		return;
	}

	priv_sock_send_result(sess->parent_fd, PRIV_SOCK_RESULT_OK);
	priv_sock_send_fd(sess->parent_fd,  fd);
	close(fd);
}

static void privop_pasv_active(session_t *sess)
{
	int active;
	if(sess->pasv_listen_fd != -1)
		active = 1;
	else
		active = 0;
	priv_sock_send_int(sess->parent_fd, active);
}

static void privop_pasv_listen(session_t *sess)
{
	char *ip = "192.168.2.106"; //暂且写死
	sess->pasv_listen_fd = tcp_server(ip, 0); //传端口0表示绑定临时端口
	
	struct sockaddr_in address;
	socklen_t addrlen = sizeof(struct sockaddr);
	if(getsockname(sess->pasv_listen_fd, (struct sockaddr*)&address, &addrlen) < 0)
		ERR_EXIT("getsockname");

	unsigned short port = ntohs(address.sin_port);
	priv_sock_send_int(sess->parent_fd, (int)port);
}

static void privop_pasv_accept(session_t *sess)
{
	int fd = accept(sess->pasv_listen_fd, 0, 0);
	close(sess->pasv_listen_fd);
	sess->pasv_listen_fd = -1;

	if(fd == -1)
	{
		priv_sock_send_result(sess->parent_fd, PRIV_SOCK_RESULT_BAD);
		return;
	}

	priv_sock_send_result(sess->parent_fd, PRIV_SOCK_RESULT_OK);
	priv_sock_send_fd(sess->parent_fd, fd);
	close(fd);
}

nobody的服务进程

ftpproto.h

 #include "ftpproto.h"
 #include"sysutil.h"
#include"privsock.h"
 #include "str.h"
 #include"ftpcods.h"
 void ftp_reply(session_t *sess , int code ,const char *text)
 {
	 char buf[MAX_BUFFER_SIZE]={0};
	 sprintf(buf, "%d %s\r\n",code,text);
	 send(sess->ctrl_fd,buf,strlen(buf),0);

 }
/////////命令映射////////////////////////////////
/////////命令映射////////////////////////////////
/////////命令映射////////////////////////////////
/////////命令映射////////////////////////////////
static void do_user(session_t *sess);
static void do_pass(session_t *sess);
static void do_syst(session_t *sess);
static void do_feat(session_t *sess);
static void do_pwd (session_t *sess);
static void do_type(session_t *sess);
static void do_port(session_t *sess);
static void do_list(session_t *sess);
static void do_pasv(session_t *sess);

static void do_cwd (session_t *sess);
static void do_mkd (session_t *sess);
static void do_rmd (session_t *sess);

static void do_rnfr(session_t *sess);
static void do_rnto(session_t *sess);
static void do_size(session_t *sess);
static void do_dele(session_t *sess);
static void do_stor(session_t *sess);
static void do_retr(session_t *sess);
static void do_rest(session_t *sess);
static void do_quit(session_t *sess);

typedef struct ftpcmd
{
	const char *cmd;
	void (*cmd_handler)(session_t *sess);
}ftpcmd_t;

static ftpcmd_t ctrl_cmds[]=
{
	{"USER",  do_user},
	{"PASS",  do_pass},
	{"SYST",  do_syst},
	{"FEAT",  do_feat},
	{"PWD",   do_pwd },
	{"TYPE",  do_type},
	{"PORT",  do_port},
	{"LIST",  do_list},
	{"PASV",  do_pasv},
	{"CWD" ,  do_cwd },
	{"MKD",   do_mkd },
	{"RMD",   do_rmd },
	{"RNFR",  do_rnfr},
	{"RNTO",  do_rnto},
	{"SIZE",  do_size},
	{"DELE",  do_dele},
	{"STOR",  do_stor},
	{"RETR",  do_retr},
	{"REST",  do_rest},
	{"QUIT",  do_quit}
 
};


//子进程服务

 void handle_child(session_t *sess)//子进程主题
 {
	
	ftp_reply(sess,220,"liu miniftp");
	int ret;
	while(1)
	 {
		memset(sess->cmdline,0,MAX_COMMAND_LINE);
		memset(sess->cmd,0,MAX_COMMAND);
		memset(sess->arg ,0 ,MAX_ARG);
		ret = recv(sess->ctrl_fd,sess->cmdline ,MAX_COMMAND_LINE,0);
		if(ret == -1)
			ERR_EXIT("readline");
		else if (ret == 0)
			exit(EXIT_SUCCESS);
		str_trim_crlf(sess->cmdline);
		// printf("cmdline = %s\n",sess->cmdline);
		str_split(sess->cmdline,sess->cmd,sess->arg,' ');
		//printf("cmd = %s\n",sess->cmd);
		//printf("arg = %s\n",sess->arg);
		int table_size =sizeof(ctrl_cmds)/sizeof(ftpcmd_t);
		int i;
		for(i=0; i<table_size;++i)
		 {
			if(strcmp(sess->cmd,ctrl_cmds[i].cmd)==0)
			 {
				if(ctrl_cmds[i].cmd_handler!= NULL)
					ctrl_cmds[i].cmd_handler(sess);
				else
					ftp_reply(sess,FTP_COMMANDNOTIMPL,"Unimplement command.");
				break;
				 
			 }
		 } 
		 if(i >= table_size)
			 ftp_reply(sess,FTP_BADCMD,"Unknown command."); 
	 } 
 } 
 static void do_user(session_t *sess)//根据用户名 确定用户是否存在
 {
	 struct passwd *pwd = getpwnam(sess->arg);
	 if(pwd== NULL)//用户名找不到uid
	 {
		ftp_reply(sess,FTP_LOGINERR ,"login incorrect.");
		return;
	 }
    sess->uid =pwd -> pw_uid;

	 ftp_reply(sess,FTP_GIVEPWORD,"please specify the password"); 
 }

static void do_pass(session_t *sess)
{  
	//鉴权
	 struct passwd *pwd = getpwuid(sess->uid);
	 if(pwd == NULL)
	{
		 	ftp_reply(sess,FTP_LOGINERR ,"login incorrect.");
			return;
  	}
	 struct spwd *spd = getspnam(pwd->pw_name);
	 if(spd == NULL)
	{
		 ftp_reply(sess,FTP_LOGINERR,"login incorrect.");
		 return;
	}
	char *encry_pwd= crypt(sess->arg,spd->sp_pwdp);
	if(strcmp(encry_pwd, spd->sp_pwdp) != 0)
	{
		ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
		return;
	}
	
	setegid(pwd->pw_gid);
	seteuid(pwd->pw_uid);
	chdir(pwd->pw_dir);

	ftp_reply(sess, FTP_LOGINOK, "Login successful.");
}
static void do_syst(session_t *sess)
{
	ftp_reply(sess, FTP_SYSTOK, "UNIX Type: L8");
}
static void do_feat(session_t *sess)
{
	send(sess->ctrl_fd, "211-Features:\r\n" ,strlen("211-Features:\r\n"), 0);
	send(sess->ctrl_fd, " EPRT\r\n", strlen(" EPRT\r\n"), 0);
	send(sess->ctrl_fd, " EPSV\r\n", strlen(" EPSV\r\n"), 0);
	send(sess->ctrl_fd, " MDTM\r\n", strlen(" MDTM\r\n"), 0);
	send(sess->ctrl_fd, " PASV\r\n", strlen(" PASV\r\n"), 0);
	send(sess->ctrl_fd, " REST STREAM\r\n", strlen(" REST STREAM\r\n"), 0);
	send(sess->ctrl_fd, "  SIZE\r\n", strlen(" SIZE\r\n"), 0);
	send(sess->ctrl_fd, " TVFS\r\n", strlen(" TVFS\r\n"), 0);
	send(sess->ctrl_fd, " UTF8\r\n", strlen(" UTF8\r\n"), 0);
	send(sess->ctrl_fd, "211 End\r\n", strlen("211 End\r\n"), 0);
}
static void do_pwd (session_t *sess)
{
	char buffer[MAX_BUFFER_SIZE] = {0};
	getcwd(buffer, MAX_BUFFER_SIZE);   // 获取目录信息
	char msg[MAX_BUFFER_SIZE] = {0};
	sprintf(msg, "\"%s\"", buffer);    //  
	ftp_reply(sess, FTP_PWDOK, msg);
}
static void do_type(session_t *sess)
{
	//TYPE A  or  TYPE I
	if(strcmp(sess->arg, "A") == 0)
	{
		sess->is_ascii = 1;
		ftp_reply(sess, FTP_TYPEOK, "Switching to ASCII mode.");
	}
	else if(strcmp(sess->arg, "I") == 0)
	{
		sess->is_ascii = 0;
		ftp_reply(sess, FTP_TYPEOK, "Switching to Binary mode.");
	}
	else
		ftp_reply(sess, FTP_BADCMD, "Unrecognised TYPE command.");
}
static void do_port(session_t *sess)//主动模式分析地址与端口号 存入sess结构体的主动连接指针
{
	//PORT 192,168,2,106,7,34
	unsigned int v[6] = {0};
	sscanf(sess->arg, "%u,%u,%u,%u,%u,%u", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5]);

	sess->port_addr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
	unsigned char *p = (unsigned char *)&sess->port_addr->sin_port;
	p[0] = v[4];
	p[1] = v[5];

	p = (unsigned char *)&sess->port_addr->sin_addr;
	p[0] = v[0];
	p[1] = v[1];
	p[2] = v[2];
	p[3] = v[3];

	sess->port_addr->sin_family = AF_INET;
	ftp_reply(sess, FTP_PORTOK, "command successful. Consider using PASV.");
}
int port_active(session_t *sess)
{
	if(sess->port_addr)
	{
		if(pasv_active(sess))
		{
			fprintf(stderr, "both port and pasv are active.");
			exit(EXIT_FAILURE);
		}
		return 1;
	}
	return 0;
}

int pasv_active(session_t *sess)
{
	priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_PASV_ACTIVE);
	if(priv_sock_get_int(sess->child_fd))
	{
		if(port_active(sess))
		{
			fprintf(stderr, "both port and pasv are active.");
			exit(EXIT_FAILURE);
		}
		return 1;
	}
	return 0; 
}
int get_port_fd(session_t *sess)
{
	int ret = 1;
	//ftp 向 nobody 发起通讯
	priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_GET_DATA_SOCK);//ftp进程向nobody发送命令 需要建立数据通信
	unsigned short port = ntohs(sess->port_addr->sin_port);// 利用会话获取端口
	char *ip = inet_ntoa(sess->port_addr->sin_addr);//利用会话获取ip

	//发送port与ip到nobody进程
	priv_sock_send_int(sess->child_fd, (int)port);//
	priv_sock_send_buf(sess->child_fd, ip, strlen(ip));//

	char res = priv_sock_get_result(sess->child_fd);
	if(res == PRIV_SOCK_RESULT_BAD)
		ret = 0;
	else if(res == PRIV_SOCK_RESULT_OK)
		sess->data_fd = priv_sock_recv_fd(sess->child_fd);
	return ret;
}

int get_pasv_fd(session_t *sess)
{
	int ret = 1;
	priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_PASV_ACCEPT);
	char res = priv_sock_get_result(sess->child_fd);
	if(res == PRIV_SOCK_RESULT_BAD)
		ret = 0;
	else if(res == PRIV_SOCK_RESULT_OK)
		sess->data_fd = priv_sock_recv_fd(sess->child_fd);
	
	return ret;
}


int get_transfer_fd(session_t *sess)
{
	if(!port_active(sess) && !pasv_active(sess))
	{
		ftp_reply(sess, FTP_BADSENDCONN,"Use PORT or PASV first.");
		return 0;
	}

	int ret = 1;
	//port
	if(port_active(sess))
	{
		if(!get_port_fd(sess))//获取主动数据连接
			ret = 0;
	}

	//pasv
	if(pasv_active(sess))
	{
		if(!get_pasv_fd(sess))//获取被动数据连
			ret = 0;
	}

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

	return ret;
}
static void list_common(session_t *sess)
{
	DIR *dir = opendir(".");//打开当前目录
	if(dir == NULL)
		return;

	//drwxr-xr-x    3 1000     1000           30 Sep 09  2019 Desktop
	char buf[MAX_BUFFER_SIZE] = {0};

	struct stat sbuf; //用于保存文件的属性
	struct dirent *dt;
	while((dt = readdir(dir)) != NULL)
	{
		if(stat(dt->d_name, &sbuf) < 0)
			continue;
		if(dt->d_name[0] == '.')  //过滤掉隐藏文件
			continue;

		memset(buf, MAX_BUFFER_SIZE, 0);
		//先组合权限
		const char *perms = statbuf_get_perms(&sbuf);  //drwxr-xr-x
		int offset = 0;
		offset += sprintf(buf, "%s", perms);
		offset += sprintf(buf+offset, "%3d %-8d %-8d %8u ", sbuf.st_nlink, sbuf.st_uid, sbuf.st_gid,sbuf.st_size);
		
		//后组合时间日期
		const char *pdate = statbuf_get_date(&sbuf);   //Sep 09  2019 
		offset += sprintf(buf+offset, "%s ", pdate);
		sprintf(buf+offset, "%s\r\n", dt->d_name);

		//发送数据
		send(sess->data_fd, buf, strlen(buf), 0);
	}
}

static void do_list(session_t *sess)//显式列表
{
	//1 建立数据连接
	if(get_transfer_fd(sess) == 0)//担当主动与被动两方面 内部分辨
		return;

	//2 回复 FTP_DATACONN 150
	ftp_reply(sess, FTP_DATACONN ,"Here comes the directory listing.");

	//3 显示列表
	list_common(sess);


	//4 关闭连接
	close(sess->data_fd);
	sess->data_fd = -1;

	//5 回复 FTP_TRANSFEROK 226
	ftp_reply(sess, FTP_TRANSFEROK, "Directory send OK.");
} 
static void do_pasv(session_t *sess)
{
	// 227 Entering Passive Mode (192,168,232,10,140,176).
	char ip[16] = "192.168.2.106"; //服务器的IP
	sess->pasv_listen_fd = tcp_server(ip, 0);//port为0代表生成临时端口号

	struct sockaddr_in address;
	socklen_t addrlen = sizeof(struct sockaddr);
	if(getsockname(sess->pasv_listen_fd, (struct sockaddr*)&address, &addrlen) < 0)
		ERR_EXIT("getsockname");

	unsigned short port = ntohs(address.sin_port);

	int v[4] = {0};
	sscanf(ip, "%u.%u.%u.%u", &v[0], &v[1], &v[2], &v[3]);
	char msg[MAX_BUFFER_SIZE] = {0};
	sprintf(msg, "Entering Passive Mode (%u,%u,%u,%u,%u,%u).", v[0],v[1],v[2],v[3], port>>8, port&0x00ff);
	ftp_reply(sess, FTP_PASVOK, msg);
}
static void do_cwd (session_t *sess)
{
	if(chdir(sess->arg) < 0)
	{
		//550 Failed to change directory.
		ftp_reply(sess, FTP_NOPERM, "Failed to change directory.");
		return;
	}
	//250 Directory successfully changed.
	ftp_reply(sess, FTP_CWDOK, "Directory successfully changed.");
}
static void do_mkd (session_t *sess)
{
	if(mkdir(sess->arg, 0777) < 0)
	{
		//550 Create directory operation failed.
		ftp_reply(sess, FTP_NOPERM, "Create directory operation failed.");
		return;
	}
	//257 "/home/bss/mytt/t1" created
	char buf[MAX_BUFFER_SIZE] = {0};
	sprintf(buf, "\"%s\" created", sess->arg);
	ftp_reply(sess, FTP_MKDIROK, buf);
}
static void do_rmd (session_t *sess)
{
	if(rmdir(sess->arg) < 0)
	{
		// 550 Remove directory operation failed.
		ftp_reply(sess, FTP_NOPERM, "Remove directory operation failed.");
		return;
	}
	// 250 Remove directory operation successful.
	ftp_reply(sess, FTP_RMDIROK, "Remove directory operation successful.");
}
static void do_dele(session_t *sess)
{
	if(unlink(sess->arg) < 0)
	{
		ftp_reply(sess, FTP_NOPERM, "Delete operation failed.");
		return;
	}
	//250 Delete operation successful.
	ftp_reply(sess, FTP_DELEOK, "Delete operation successful.");
}
static void do_rnfr (session_t *sess)//重命名 准备修改
{
	sess->rnfr_name = (char*)malloc(strlen(sess->arg)+1);
	memset(sess->rnfr_name, 0, strlen(sess->arg)+1);
	strcpy(sess->rnfr_name, sess->arg);
	ftp_reply(sess, FTP_RNFROK, "Ready for RNTO.");
}
static void do_rnto (session_t *sess)//
{
	if(sess->rnfr_name == NULL)
	{
		//503 RNFR required first.
		ftp_reply(sess, FTP_NEEDRNFR, "RNFR required first.");
		return;
	}

	if(rename(sess->rnfr_name, sess->arg) < 0)//旧文件名到新文件名错误
	{
		ftp_reply(sess, FTP_NOPERM, "Rename failed.");
		free(sess->rnfr_name);
		sess->rnfr_name = NULL;
		return;
	}
	
	free(sess->rnfr_name);
	sess->rnfr_name = NULL;
	
	ftp_reply(sess, FTP_RENAMEOK, "Rename successful.");
}
static void do_size(session_t *sess)//查看文件的大小
{
	struct stat sbuf;
	if(stat(sess->arg, &sbuf) < 0)
	{
		//550 Could not get file size.
		ftp_reply(sess, FTP_FILEFAIL, "Could not get file size.");
		return;
	}

	if(!S_ISREG(sbuf.st_mode))
	{
		ftp_reply(sess, FTP_FILEFAIL, "Could not get file size.");
		return;
	}

	char buf[MAX_BUFFER_SIZE] = {0};
	sprintf(buf, "%d", sbuf.st_size);
	ftp_reply(sess, FTP_SIZEOK, buf);
}
static void do_stor(session_t *sess)//上传文件
{
	//建立数据连接
	if(get_transfer_fd(sess) == 0)
		return;

	int fd = open(sess->arg,  O_CREAT|O_WRONLY, 0755);
	if(fd == -1)
	{
		ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
		return;
	}
	
	ftp_reply(sess, FTP_DATACONN,"Ok to send data.");

	long long offset = sess->restart_pos;
	sess->restart_pos = 0;
	if(lseek(fd, offset, SEEK_SET) < 0)//定位文件指针的位置
	{
		ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
		return;
	}

	char buf[MAX_BUFFER_SIZE] = {0};
	int ret;
	while(1)
	{
		ret = recv(sess->data_fd, buf, MAX_BUFFER_SIZE, 0);
		if(ret == -1)
		{
			ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file.");
			break;
		}
		if(ret == 0)//传输完成
		{
			//226 Transfer complete.
			ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
			break;
		}

		if(write(fd, buf, ret) != ret)
		{
			ftp_reply(sess, FTP_BADSENDFILE, "Failure writting to network stream.");
			break;
		}
	}

	close(fd);
	close(sess->data_fd);
	sess->data_fd = -1;
}
static void do_retr(session_t *sess)//下载文件
{
	//建立数据连接
	if(get_transfer_fd(sess) == 0)
		return;

	int fd = open(sess->arg, O_RDONLY);
	if(fd == -1)
	{
		ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
		return;
	}

	struct stat sbuf;
	fstat(fd, &sbuf);

	//断点续载
	long long offset = sess->restart_pos;
	sess->restart_pos = 0;
	if(offset >= sbuf.st_size)
	{
		ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
	}
	else
	{
		char msg[MAX_BUFFER_SIZE] = {0};
		if(sess->is_ascii)
			sprintf(msg, "Opening ASCII mode data connection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size);
		else
			sprintf(msg, "Opening BINARY mode data connection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size);
		// 150 Opening ASCII mode data connection for /home/bss/mytt/abc/test.cpp (70 bytes).
		ftp_reply(sess, FTP_DATACONN, msg);

		if(lseek(fd, offset, SEEK_SET) < 0)
		{
			ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
			return;
		}
			
		char buf[MAX_BUFFER_SIZE] = {0};
		long long read_total_bytes = (long long)sbuf.st_size - offset;
		int read_count = 0;
		int ret;
		while(1)
		{
			read_count = read_total_bytes > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : read_total_bytes;
			ret = read(fd, buf, read_count);
			if(ret==-1 || ret!=read_count)
			{
				ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file.");
				break;
			}
			if(ret == 0)
			{
				// 226 Transfer complete.
				ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
				break;
			}

			//限速
		

			if(send(sess->data_fd, buf, ret, 0) != ret)
			{
				ftp_reply(sess, FTP_BADSENDFILE, "Failure writting to network stream.");
				break;
			}
			read_total_bytes -= read_count;
		}
	}

	close(fd);
	close(sess->data_fd);
	sess->data_fd = -1;
}
static void do_rest(session_t *sess)
{
	sess->restart_pos = (long long)atoll(sess->arg);

	//350 Restart position accepted (1612906496).
	char msg[MAX_BUFFER_SIZE] = {0};
	sprintf(msg, "Restart position accepted (%lld).", sess->restart_pos);
	ftp_reply(sess, FTP_RESTOK, msg);
}

static void do_quit(session_t *sess)
{
	ftp_reply(sess, FTP_GOODBYE, "Goodbye.");
}
```python
### ftpproto.c
```python
 #include "ftpproto.h"
 #include"sysutil.h"
#include"privsock.h"
 #include "str.h"
 #include"ftpcods.h"
 void ftp_reply(session_t *sess , int code ,const char *text)
 {
	 char buf[MAX_BUFFER_SIZE]={0};
	 sprintf(buf, "%d %s\r\n",code,text);
	 send(sess->ctrl_fd,buf,strlen(buf),0);

 }
/////////命令映射////////////////////////////////
/////////命令映射////////////////////////////////
/////////命令映射////////////////////////////////
/////////命令映射////////////////////////////////
static void do_user(session_t *sess);
static void do_pass(session_t *sess);
static void do_syst(session_t *sess);
static void do_feat(session_t *sess);
static void do_pwd (session_t *sess);
static void do_type(session_t *sess);
static void do_port(session_t *sess);
static void do_list(session_t *sess);
static void do_pasv(session_t *sess);

static void do_cwd (session_t *sess);
static void do_mkd (session_t *sess);
static void do_rmd (session_t *sess);

static void do_rnfr(session_t *sess);
static void do_rnto(session_t *sess);
static void do_size(session_t *sess);
static void do_dele(session_t *sess);
static void do_stor(session_t *sess);
static void do_retr(session_t *sess);
static void do_rest(session_t *sess);
static void do_quit(session_t *sess);

typedef struct ftpcmd
{
	const char *cmd;
	void (*cmd_handler)(session_t *sess);
}ftpcmd_t;

static ftpcmd_t ctrl_cmds[]=
{
	{"USER",  do_user},
	{"PASS",  do_pass},
	{"SYST",  do_syst},
	{"FEAT",  do_feat},
	{"PWD",   do_pwd },
	{"TYPE",  do_type},
	{"PORT",  do_port},
	{"LIST",  do_list},
	{"PASV",  do_pasv},
	{"CWD" ,  do_cwd },
	{"MKD",   do_mkd },
	{"RMD",   do_rmd },
	{"RNFR",  do_rnfr},
	{"RNTO",  do_rnto},
	{"SIZE",  do_size},
	{"DELE",  do_dele},
	{"STOR",  do_stor},
	{"RETR",  do_retr},
	{"REST",  do_rest},
	{"QUIT",  do_quit}
 
};


//子进程服务

 void handle_child(session_t *sess)//子进程主题
 {
	
	ftp_reply(sess,220,"liu miniftp");
	int ret;
	while(1)
	 {
		memset(sess->cmdline,0,MAX_COMMAND_LINE);
		memset(sess->cmd,0,MAX_COMMAND);
		memset(sess->arg ,0 ,MAX_ARG);
		ret = recv(sess->ctrl_fd,sess->cmdline ,MAX_COMMAND_LINE,0);
		if(ret == -1)
			ERR_EXIT("readline");
		else if (ret == 0)
			exit(EXIT_SUCCESS);
		str_trim_crlf(sess->cmdline);
		// printf("cmdline = %s\n",sess->cmdline);
		str_split(sess->cmdline,sess->cmd,sess->arg,' ');
		//printf("cmd = %s\n",sess->cmd);
		//printf("arg = %s\n",sess->arg);
		int table_size =sizeof(ctrl_cmds)/sizeof(ftpcmd_t);
		int i;
		for(i=0; i<table_size;++i)
		 {
			if(strcmp(sess->cmd,ctrl_cmds[i].cmd)==0)
			 {
				if(ctrl_cmds[i].cmd_handler!= NULL)
					ctrl_cmds[i].cmd_handler(sess);
				else
					ftp_reply(sess,FTP_COMMANDNOTIMPL,"Unimplement command.");
				break;
				 
			 }
		 } 
		 if(i >= table_size)
			 ftp_reply(sess,FTP_BADCMD,"Unknown command."); 
	 } 
 } 
 static void do_user(session_t *sess)//根据用户名 确定用户是否存在
 {
	 struct passwd *pwd = getpwnam(sess->arg);
	 if(pwd== NULL)//用户名找不到uid
	 {
		ftp_reply(sess,FTP_LOGINERR ,"login incorrect.");
		return;
	 }
    sess->uid =pwd -> pw_uid;

	 ftp_reply(sess,FTP_GIVEPWORD,"please specify the password"); 
 }

static void do_pass(session_t *sess)
{  
	//鉴权
	 struct passwd *pwd = getpwuid(sess->uid);
	 if(pwd == NULL)
	{
		 	ftp_reply(sess,FTP_LOGINERR ,"login incorrect.");
			return;
  	}
	 struct spwd *spd = getspnam(pwd->pw_name);
	 if(spd == NULL)
	{
		 ftp_reply(sess,FTP_LOGINERR,"login incorrect.");
		 return;
	}
	char *encry_pwd= crypt(sess->arg,spd->sp_pwdp);
	if(strcmp(encry_pwd, spd->sp_pwdp) != 0)
	{
		ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
		return;
	}
	
	setegid(pwd->pw_gid);
	seteuid(pwd->pw_uid);
	chdir(pwd->pw_dir);

	ftp_reply(sess, FTP_LOGINOK, "Login successful.");
}
static void do_syst(session_t *sess)
{
	ftp_reply(sess, FTP_SYSTOK, "UNIX Type: L8");
}
static void do_feat(session_t *sess)
{
	send(sess->ctrl_fd, "211-Features:\r\n" ,strlen("211-Features:\r\n"), 0);
	send(sess->ctrl_fd, " EPRT\r\n", strlen(" EPRT\r\n"), 0);
	send(sess->ctrl_fd, " EPSV\r\n", strlen(" EPSV\r\n"), 0);
	send(sess->ctrl_fd, " MDTM\r\n", strlen(" MDTM\r\n"), 0);
	send(sess->ctrl_fd, " PASV\r\n", strlen(" PASV\r\n"), 0);
	send(sess->ctrl_fd, " REST STREAM\r\n", strlen(" REST STREAM\r\n"), 0);
	send(sess->ctrl_fd, "  SIZE\r\n", strlen(" SIZE\r\n"), 0);
	send(sess->ctrl_fd, " TVFS\r\n", strlen(" TVFS\r\n"), 0);
	send(sess->ctrl_fd, " UTF8\r\n", strlen(" UTF8\r\n"), 0);
	send(sess->ctrl_fd, "211 End\r\n", strlen("211 End\r\n"), 0);
}
static void do_pwd (session_t *sess)
{
	char buffer[MAX_BUFFER_SIZE] = {0};
	getcwd(buffer, MAX_BUFFER_SIZE);   // 获取目录信息
	char msg[MAX_BUFFER_SIZE] = {0};
	sprintf(msg, "\"%s\"", buffer);    //  
	ftp_reply(sess, FTP_PWDOK, msg);
}
static void do_type(session_t *sess)
{
	//TYPE A  or  TYPE I
	if(strcmp(sess->arg, "A") == 0)
	{
		sess->is_ascii = 1;
		ftp_reply(sess, FTP_TYPEOK, "Switching to ASCII mode.");
	}
	else if(strcmp(sess->arg, "I") == 0)
	{
		sess->is_ascii = 0;
		ftp_reply(sess, FTP_TYPEOK, "Switching to Binary mode.");
	}
	else
		ftp_reply(sess, FTP_BADCMD, "Unrecognised TYPE command.");
}
static void do_port(session_t *sess)//主动模式分析地址与端口号 存入sess结构体的主动连接指针
{
	//PORT 192,168,2,106,7,34
	unsigned int v[6] = {0};
	sscanf(sess->arg, "%u,%u,%u,%u,%u,%u", &v[0],&v[1],&v[2],&v[3],&v[4],&v[5]);

	sess->port_addr = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
	unsigned char *p = (unsigned char *)&sess->port_addr->sin_port;
	p[0] = v[4];
	p[1] = v[5];

	p = (unsigned char *)&sess->port_addr->sin_addr;
	p[0] = v[0];
	p[1] = v[1];
	p[2] = v[2];
	p[3] = v[3];

	sess->port_addr->sin_family = AF_INET;
	ftp_reply(sess, FTP_PORTOK, "command successful. Consider using PASV.");
}
int port_active(session_t *sess)
{
	if(sess->port_addr)
	{
		if(pasv_active(sess))
		{
			fprintf(stderr, "both port and pasv are active.");
			exit(EXIT_FAILURE);
		}
		return 1;
	}
	return 0;
}

int pasv_active(session_t *sess)
{
	priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_PASV_ACTIVE);
	if(priv_sock_get_int(sess->child_fd))
	{
		if(port_active(sess))
		{
			fprintf(stderr, "both port and pasv are active.");
			exit(EXIT_FAILURE);
		}
		return 1;
	}
	return 0; 
}
int get_port_fd(session_t *sess)
{
	int ret = 1;
	//ftp 向 nobody 发起通讯
	priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_GET_DATA_SOCK);//ftp进程向nobody发送命令 需要建立数据通信
	unsigned short port = ntohs(sess->port_addr->sin_port);// 利用会话获取端口
	char *ip = inet_ntoa(sess->port_addr->sin_addr);//利用会话获取ip

	//发送port与ip到nobody进程
	priv_sock_send_int(sess->child_fd, (int)port);//
	priv_sock_send_buf(sess->child_fd, ip, strlen(ip));//

	char res = priv_sock_get_result(sess->child_fd);
	if(res == PRIV_SOCK_RESULT_BAD)
		ret = 0;
	else if(res == PRIV_SOCK_RESULT_OK)
		sess->data_fd = priv_sock_recv_fd(sess->child_fd);
	return ret;
}

int get_pasv_fd(session_t *sess)
{
	int ret = 1;
	priv_sock_send_cmd(sess->child_fd, PRIV_SOCK_PASV_ACCEPT);
	char res = priv_sock_get_result(sess->child_fd);
	if(res == PRIV_SOCK_RESULT_BAD)
		ret = 0;
	else if(res == PRIV_SOCK_RESULT_OK)
		sess->data_fd = priv_sock_recv_fd(sess->child_fd);
	
	return ret;
}


int get_transfer_fd(session_t *sess)
{
	if(!port_active(sess) && !pasv_active(sess))
	{
		ftp_reply(sess, FTP_BADSENDCONN,"Use PORT or PASV first.");
		return 0;
	}

	int ret = 1;
	//port
	if(port_active(sess))
	{
		if(!get_port_fd(sess))//获取主动数据连接
			ret = 0;
	}

	//pasv
	if(pasv_active(sess))
	{
		if(!get_pasv_fd(sess))//获取被动数据连
			ret = 0;
	}

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

	return ret;
}
static void list_common(session_t *sess)
{
	DIR *dir = opendir(".");//打开当前目录
	if(dir == NULL)
		return;

	//drwxr-xr-x    3 1000     1000           30 Sep 09  2019 Desktop
	char buf[MAX_BUFFER_SIZE] = {0};

	struct stat sbuf; //用于保存文件的属性
	struct dirent *dt;
	while((dt = readdir(dir)) != NULL)
	{
		if(stat(dt->d_name, &sbuf) < 0)
			continue;
		if(dt->d_name[0] == '.')  //过滤掉隐藏文件
			continue;

		memset(buf, MAX_BUFFER_SIZE, 0);
		//先组合权限
		const char *perms = statbuf_get_perms(&sbuf);  //drwxr-xr-x
		int offset = 0;
		offset += sprintf(buf, "%s", perms);
		offset += sprintf(buf+offset, "%3d %-8d %-8d %8u ", sbuf.st_nlink, sbuf.st_uid, sbuf.st_gid,sbuf.st_size);
		
		//后组合时间日期
		const char *pdate = statbuf_get_date(&sbuf);   //Sep 09  2019 
		offset += sprintf(buf+offset, "%s ", pdate);
		sprintf(buf+offset, "%s\r\n", dt->d_name);

		//发送数据
		send(sess->data_fd, buf, strlen(buf), 0);
	}
}

static void do_list(session_t *sess)//显式列表
{
	//1 建立数据连接
	if(get_transfer_fd(sess) == 0)//担当主动与被动两方面 内部分辨
		return;

	//2 回复 FTP_DATACONN 150
	ftp_reply(sess, FTP_DATACONN ,"Here comes the directory listing.");

	//3 显示列表
	list_common(sess);


	//4 关闭连接
	close(sess->data_fd);
	sess->data_fd = -1;

	//5 回复 FTP_TRANSFEROK 226
	ftp_reply(sess, FTP_TRANSFEROK, "Directory send OK.");
} 
static void do_pasv(session_t *sess)
{
	// 227 Entering Passive Mode (192,168,232,10,140,176).
	char ip[16] = "192.168.2.106"; //服务器的IP
	sess->pasv_listen_fd = tcp_server(ip, 0);//port为0代表生成临时端口号

	struct sockaddr_in address;
	socklen_t addrlen = sizeof(struct sockaddr);
	if(getsockname(sess->pasv_listen_fd, (struct sockaddr*)&address, &addrlen) < 0)
		ERR_EXIT("getsockname");

	unsigned short port = ntohs(address.sin_port);

	int v[4] = {0};
	sscanf(ip, "%u.%u.%u.%u", &v[0], &v[1], &v[2], &v[3]);
	char msg[MAX_BUFFER_SIZE] = {0};
	sprintf(msg, "Entering Passive Mode (%u,%u,%u,%u,%u,%u).", v[0],v[1],v[2],v[3], port>>8, port&0x00ff);
	ftp_reply(sess, FTP_PASVOK, msg);
}
static void do_cwd (session_t *sess)
{
	if(chdir(sess->arg) < 0)
	{
		//550 Failed to change directory.
		ftp_reply(sess, FTP_NOPERM, "Failed to change directory.");
		return;
	}
	//250 Directory successfully changed.
	ftp_reply(sess, FTP_CWDOK, "Directory successfully changed.");
}
static void do_mkd (session_t *sess)
{
	if(mkdir(sess->arg, 0777) < 0)
	{
		//550 Create directory operation failed.
		ftp_reply(sess, FTP_NOPERM, "Create directory operation failed.");
		return;
	}
	//257 "/home/bss/mytt/t1" created
	char buf[MAX_BUFFER_SIZE] = {0};
	sprintf(buf, "\"%s\" created", sess->arg);
	ftp_reply(sess, FTP_MKDIROK, buf);
}
static void do_rmd (session_t *sess)
{
	if(rmdir(sess->arg) < 0)
	{
		// 550 Remove directory operation failed.
		ftp_reply(sess, FTP_NOPERM, "Remove directory operation failed.");
		return;
	}
	// 250 Remove directory operation successful.
	ftp_reply(sess, FTP_RMDIROK, "Remove directory operation successful.");
}
static void do_dele(session_t *sess)
{
	if(unlink(sess->arg) < 0)
	{
		ftp_reply(sess, FTP_NOPERM, "Delete operation failed.");
		return;
	}
	//250 Delete operation successful.
	ftp_reply(sess, FTP_DELEOK, "Delete operation successful.");
}
static void do_rnfr (session_t *sess)//重命名 准备修改
{
	sess->rnfr_name = (char*)malloc(strlen(sess->arg)+1);
	memset(sess->rnfr_name, 0, strlen(sess->arg)+1);
	strcpy(sess->rnfr_name, sess->arg);
	ftp_reply(sess, FTP_RNFROK, "Ready for RNTO.");
}
static void do_rnto (session_t *sess)//
{
	if(sess->rnfr_name == NULL)
	{
		//503 RNFR required first.
		ftp_reply(sess, FTP_NEEDRNFR, "RNFR required first.");
		return;
	}

	if(rename(sess->rnfr_name, sess->arg) < 0)//旧文件名到新文件名错误
	{
		ftp_reply(sess, FTP_NOPERM, "Rename failed.");
		free(sess->rnfr_name);
		sess->rnfr_name = NULL;
		return;
	}
	
	free(sess->rnfr_name);
	sess->rnfr_name = NULL;
	
	ftp_reply(sess, FTP_RENAMEOK, "Rename successful.");
}
static void do_size(session_t *sess)//查看文件的大小
{
	struct stat sbuf;
	if(stat(sess->arg, &sbuf) < 0)
	{
		//550 Could not get file size.
		ftp_reply(sess, FTP_FILEFAIL, "Could not get file size.");
		return;
	}

	if(!S_ISREG(sbuf.st_mode))
	{
		ftp_reply(sess, FTP_FILEFAIL, "Could not get file size.");
		return;
	}

	char buf[MAX_BUFFER_SIZE] = {0};
	sprintf(buf, "%d", sbuf.st_size);
	ftp_reply(sess, FTP_SIZEOK, buf);
}
static void do_stor(session_t *sess)//上传文件
{
	//建立数据连接
	if(get_transfer_fd(sess) == 0)
		return;

	int fd = open(sess->arg,  O_CREAT|O_WRONLY, 0755);
	if(fd == -1)
	{
		ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
		return;
	}
	
	ftp_reply(sess, FTP_DATACONN,"Ok to send data.");

	long long offset = sess->restart_pos;
	sess->restart_pos = 0;
	if(lseek(fd, offset, SEEK_SET) < 0)//定位文件指针的位置
	{
		ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
		return;
	}

	char buf[MAX_BUFFER_SIZE] = {0};
	int ret;
	while(1)
	{
		ret = recv(sess->data_fd, buf, MAX_BUFFER_SIZE, 0);
		if(ret == -1)
		{
			ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file.");
			break;
		}
		if(ret == 0)//传输完成
		{
			//226 Transfer complete.
			ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
			break;
		}

		if(write(fd, buf, ret) != ret)
		{
			ftp_reply(sess, FTP_BADSENDFILE, "Failure writting to network stream.");
			break;
		}
	}

	close(fd);
	close(sess->data_fd);
	sess->data_fd = -1;
}
static void do_retr(session_t *sess)//下载文件
{
	//建立数据连接
	if(get_transfer_fd(sess) == 0)
		return;

	int fd = open(sess->arg, O_RDONLY);
	if(fd == -1)
	{
		ftp_reply(sess, FTP_FILEFAIL, "Failed to open file.");
		return;
	}

	struct stat sbuf;
	fstat(fd, &sbuf);

	//断点续载
	long long offset = sess->restart_pos;
	sess->restart_pos = 0;
	if(offset >= sbuf.st_size)
	{
		ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
	}
	else
	{
		char msg[MAX_BUFFER_SIZE] = {0};
		if(sess->is_ascii)
			sprintf(msg, "Opening ASCII mode data connection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size);
		else
			sprintf(msg, "Opening BINARY mode data connection for %s (%lld bytes).", sess->arg, (long long)sbuf.st_size);
		// 150 Opening ASCII mode data connection for /home/bss/mytt/abc/test.cpp (70 bytes).
		ftp_reply(sess, FTP_DATACONN, msg);

		if(lseek(fd, offset, SEEK_SET) < 0)
		{
			ftp_reply(sess, FTP_UPLOADFAIL, "Could not create file.");
			return;
		}
			
		char buf[MAX_BUFFER_SIZE] = {0};
		long long read_total_bytes = (long long)sbuf.st_size - offset;
		int read_count = 0;
		int ret;
		while(1)
		{
			read_count = read_total_bytes > MAX_BUFFER_SIZE ? MAX_BUFFER_SIZE : read_total_bytes;
			ret = read(fd, buf, read_count);
			if(ret==-1 || ret!=read_count)
			{
				ftp_reply(sess, FTP_BADSENDFILE, "Failure reading from local file.");
				break;
			}
			if(ret == 0)
			{
				// 226 Transfer complete.
				ftp_reply(sess, FTP_TRANSFEROK, "Transfer complete.");
				break;
			}

			//限速
		

			if(send(sess->data_fd, buf, ret, 0) != ret)
			{
				ftp_reply(sess, FTP_BADSENDFILE, "Failure writting to network stream.");
				break;
			}
			read_total_bytes -= read_count;
		}
	}

	close(fd);
	close(sess->data_fd);
	sess->data_fd = -1;
}
static void do_rest(session_t *sess)
{
	sess->restart_pos = (long long)atoll(sess->arg);

	//350 Restart position accepted (1612906496).
	char msg[MAX_BUFFER_SIZE] = {0};
	sprintf(msg, "Restart position accepted (%lld).", sess->restart_pos);
	ftp_reply(sess, FTP_RESTOK, msg);
}

static void do_quit(session_t *sess)
{
	ftp_reply(sess, FTP_GOODBYE, "Goodbye.");
}

进程间通信命令处理函数

privsock.h

#include"privsock.h"
#include"sysutil.h"

void priv_sock_init(session_t *sess)//进程间通信 初始化
{
	int sockfds[2];
	if(socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds) < 0)
		ERR_EXIT("socketpair");
	sess->child_fd = sockfds[1];
	sess->parent_fd = sockfds[0];
}
void priv_sock_close(session_t *sess)
{
	if(sess->parent_fd != -1)
	{
		close(sess->parent_fd);
		sess->parent_fd = -1;
	}
	if(sess->child_fd != -1)
	{
		close(sess->child_fd);
		sess->child_fd = -1;
	}
}
void priv_sock_set_parent_context(session_t *sess)//关闭右边端口的使用权 (子进程中意思就是连接父进程)
{
	if(sess->child_fd != -1)
	{
		close(sess->child_fd);
		sess->child_fd = -1;
	}
}
void priv_sock_set_child_context(session_t *sess)//
{
	if(sess->parent_fd != -1)
	{
		close(sess->parent_fd);
		sess->parent_fd = -1;
	}
}

void priv_sock_send_cmd(int fd, char cmd)//向进程id发送命令
{
	int ret = send(fd, &cmd, sizeof(cmd), 0);
	if(ret != sizeof(cmd))
		ERR_EXIT("priv_sock_send_cmd error.");
}
char priv_sock_get_cmd(int fd)//接收进程命令并且阻塞
{
	char cmd;
	int ret;
	ret = recv(fd, &cmd, sizeof(cmd), 0);
	if(ret == 0)
	{
		printf("ftp process exit.\n");
		exit(EXIT_SUCCESS);
	}
	if(ret != sizeof(cmd))
		ERR_EXIT("priv_sock_get_cmd error.");
	return cmd;
}

void priv_sock_send_result(int fd, char res)//发送字符
{
	int ret = send(fd, &res, sizeof(res), 0);
	if(ret != sizeof(res))
		ERR_EXIT("priv_sock_send_result error.");
}
char priv_sock_get_result(int fd)//获取字符
{
	char res;
	int ret;
	ret = recv(fd, &res, sizeof(res), 0);
	if(ret == 0)
	{
		printf("ftp process exit.\n");
		exit(EXIT_SUCCESS);
	}
	if(ret != sizeof(res))
		ERR_EXIT("priv_sock_get_result error.");
	return res;
}

void priv_sock_send_int(int fd, int the_int)//发送整形
{
	int ret = send(fd, &the_int, sizeof(the_int), 0);
	if(ret != sizeof(the_int))
		ERR_EXIT("priv_sock_send_int error.");
}
int priv_sock_get_int(int fd)//获取整形
{
	int res;
	int ret;
	ret = recv(fd, &res, sizeof(res), 0);
	if(ret == 0)
	{
		printf("ftp process exit.\n");
		exit(EXIT_SUCCESS);
	}
	if(ret != sizeof(res))
		ERR_EXIT("priv_sock_get_int error.");
	return res;
}

void priv_sock_send_buf(int fd, const char *buf, unsigned int len)//发送字符串
{
	priv_sock_send_int(fd, len);//发送要发送的长度
	int ret = send(fd, buf, len, 0);//发送字符串
	if(ret != len)//发送的长度不等于字符串长度报错
		ERR_EXIT("priv_sock_send_buf error.");
}
void priv_sock_recv_buf(int fd, char *buf, unsigned int len)
{
	unsigned int recv_len = priv_sock_get_int(fd);//获取要收到的字符长度
	int ret = recv(fd, buf, recv_len, 0);//获取接收字符串
	if(ret != recv_len)
		ERR_EXIT("priv_sock_recv_buf error.");
}

void priv_sock_send_fd(int sock_fd, int fd)//描述符发送
{
	send_fd(sock_fd, fd);
}
int priv_sock_recv_fd(int sock_fd)
{
	return recv_fd(sock_fd);
}

privsock.c

#include"privsock.h"
#include"sysutil.h"

void priv_sock_init(session_t *sess)//进程间通信 初始化
{
	int sockfds[2];
	if(socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds) < 0)
		ERR_EXIT("socketpair");
	sess->child_fd = sockfds[1];
	sess->parent_fd = sockfds[0];
}
void priv_sock_close(session_t *sess)
{
	if(sess->parent_fd != -1)
	{
		close(sess->parent_fd);
		sess->parent_fd = -1;
	}
	if(sess->child_fd != -1)
	{
		close(sess->child_fd);
		sess->child_fd = -1;
	}
}
void priv_sock_set_parent_context(session_t *sess)//关闭右边端口的使用权 (子进程中意思就是连接父进程)
{
	if(sess->child_fd != -1)
	{
		close(sess->child_fd);
		sess->child_fd = -1;
	}
}
void priv_sock_set_child_context(session_t *sess)//
{
	if(sess->parent_fd != -1)
	{
		close(sess->parent_fd);
		sess->parent_fd = -1;
	}
}

void priv_sock_send_cmd(int fd, char cmd)//向进程id发送命令
{
	int ret = send(fd, &cmd, sizeof(cmd), 0);
	if(ret != sizeof(cmd))
		ERR_EXIT("priv_sock_send_cmd error.");
}
char priv_sock_get_cmd(int fd)//接收进程命令并且阻塞
{
	char cmd;
	int ret;
	ret = recv(fd, &cmd, sizeof(cmd), 0);
	if(ret == 0)
	{
		printf("ftp process exit.\n");
		exit(EXIT_SUCCESS);
	}
	if(ret != sizeof(cmd))
		ERR_EXIT("priv_sock_get_cmd error.");
	return cmd;
}

void priv_sock_send_result(int fd, char res)//发送字符
{
	int ret = send(fd, &res, sizeof(res), 0);
	if(ret != sizeof(res))
		ERR_EXIT("priv_sock_send_result error.");
}
char priv_sock_get_result(int fd)//获取字符
{
	char res;
	int ret;
	ret = recv(fd, &res, sizeof(res), 0);
	if(ret == 0)
	{
		printf("ftp process exit.\n");
		exit(EXIT_SUCCESS);
	}
	if(ret != sizeof(res))
		ERR_EXIT("priv_sock_get_result error.");
	return res;
}

void priv_sock_send_int(int fd, int the_int)//发送整形
{
	int ret = send(fd, &the_int, sizeof(the_int), 0);
	if(ret != sizeof(the_int))
		ERR_EXIT("priv_sock_send_int error.");
}
int priv_sock_get_int(int fd)//获取整形
{
	int res;
	int ret;
	ret = recv(fd, &res, sizeof(res), 0);
	if(ret == 0)
	{
		printf("ftp process exit.\n");
		exit(EXIT_SUCCESS);
	}
	if(ret != sizeof(res))
		ERR_EXIT("priv_sock_get_int error.");
	return res;
}

void priv_sock_send_buf(int fd, const char *buf, unsigned int len)//发送字符串
{
	priv_sock_send_int(fd, len);//发送要发送的长度
	int ret = send(fd, buf, len, 0);//发送字符串
	if(ret != len)//发送的长度不等于字符串长度报错
		ERR_EXIT("priv_sock_send_buf error.");
}
void priv_sock_recv_buf(int fd, char *buf, unsigned int len)
{
	unsigned int recv_len = priv_sock_get_int(fd);//获取要收到的字符长度
	int ret = recv(fd, buf, recv_len, 0);//获取接收字符串
	if(ret != recv_len)
		ERR_EXIT("priv_sock_recv_buf error.");
}

void priv_sock_send_fd(int sock_fd, int fd)//描述符发送
{
	send_fd(sock_fd, fd);
}
int priv_sock_recv_fd(int sock_fd)
{
	return recv_fd(sock_fd);
}

makefile 文件

CC = gcc
CFLAGS = -g
OBJS = sysutil.o session.o ftpproto.o privparent.o bitminiftp.o str.o privsock.o
LIBS = -lcrypt
BIN = bitminiftp

$(BIN):$(OBJS)
	$(CC) $(CFLAGS) $^ -o $@ $(LIBS)
%.o:%.c
	$(CC) $(CFLAGS) -c $< -o $@

.PHONY:clean
clean :
	rm -fr *.o $(BIN)

4.源码下载与视频演示

4.1百度云链接

链接:https://pan.baidu.com/s/1jpY40O6n1-75F-z7qEXHHg
提取码:07uv

4.2github链接

https://github.com/dongdapixiu/minifto

4.3视频演示

https://v.youku.com/v_show/id_XNTEyMTE4ODkwMA==.html

在此输入正文

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值