⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 myftp_state_machine.c

📁 Linux下的ftp服务器
💻 C
📖 第 1 页 / 共 4 页
字号:
{
	return myftp_cmd_handle_cwd(p_thesession);
	
}

/*===============================================================================
function:返回上一级目录
===============================================================================*/
int myftp_cmd_handle_cdup(pMYFTP_SESSION p_thesession)
{
	if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
	{
		write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
		return -1;
	}
	chdir("..");
		
	write(p_thesession->cmd_cntl_fd, "250 Directory successfully changed.\r\n",strlen("250 Directory successfully changed.\r\n"));
	return -1;
}

/*===============================================================================
function:QUIT客户端退出登录命令处理函数
注:此命令表示控制连接也断开
===============================================================================*/
int myftp_cmd_handle_quit(pMYFTP_SESSION p_thesession)
{
	write(p_thesession->cmd_cntl_fd, "221 Goodbye.\r\n", strlen("221 Goodbye.\r\n"));
	return CHILD_QUIT;
}

/*===============================================================================
function:注销命令
注:此命令执行后,控制连接仍在,相当于电脑的注销后切换用户命令
===============================================================================*/
int myftp_cmd_handle_rein(pMYFTP_SESSION p_thesession)
{
	int cmd_cntl_sock=p_thesession->cmd_cntl_fd;
	write(p_thesession->cmd_cntl_fd, "220 Service ready for new user.\r\n", strlen("220 Service ready for new user.\r\n"));

	return cmd_cntl_sock;
}

/*===============================================================================
function:port模式处理函数
注: port模式即为服务端产生一个新的数据套接字,主动去连接客户端的数据端口
===============================================================================*/
int myftp_cmd_handle_port(pMYFTP_SESSION p_thesession)
{
	if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
	{
		write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
		return -1;
	}
	myftp_arg_to_addr(p_thesession);

	myftp_make_port_data_socket(p_thesession);

	p_thesession->is_port_or_pasv=PORT;//TRUE===>port mode。
	
	int client_sockfd=connect(p_thesession->port_data_fd, &p_thesession->data_sockaddr.un.u_sockaddr, sizeof(MYFTP_SOCKADDR));

	if(-1==client_sockfd)
	{
		puts(p_thesession->p_cmd_line->cmd_line);
		die("myftp_cmd_handle_port() connect error.\n");
	}
	write(p_thesession->cmd_cntl_fd,"200 PORT command successful. Consider using PASV.\r\n",strlen("200 PORT command successful. Consider using PASV.\r\n"));
	return -1;
}

/*===============================================================================
function:将形如192.168.168.203的字符串转换为192,168,168,203字符串
===============================================================================*/
char* myftp_to_dotstr(char* p_str)
{
	int i=0;
	while (*(p_str+i))
	{
		if (*(p_str+i) == '.')
		{
			*(p_str+i) = ',';
		}
		i++;
	}
	return p_str;
}

/*===============================================================================
function:创建数据连接通道
注:过程==》创建流式套接字-根据控制连接套接字获取本机IP并存放与临时SOCKADDR结构中-制定该端口号为0-bind套接字到该地址上
	再次获取本机ip以得到一个随机的端口号,从而完全生成了数据连接套接字。
===============================================================================*/
int myftp_make_pasv_data_fd(pMYFTP_SESSION p_thesession)
{
	int pasv_data_sock=socket(AF_INET,SOCK_STREAM,0);//数据端口套节字;
	if (-1==pasv_data_sock)
	{
		die("socket data error.\n");
	}
	MYFTP_SOCKADDR tmp_data_sock;
	int len=sizeof(MYFTP_SOCKADDR);
	getsockname(p_thesession->cmd_cntl_fd,&tmp_data_sock.un.u_sockaddr,&len);
	tmp_data_sock.un.u_sockaddr_in.sin_port=0;
	tmp_data_sock.un.u_sockaddr_in.sin_family=AF_INET;
	if (-1==bind(pasv_data_sock,&tmp_data_sock.un.u_sockaddr,len))
	{
		die("bind error.\n");
	}
	getsockname(pasv_data_sock,&tmp_data_sock.un.u_sockaddr,&len);
	p_thesession->local_addr=tmp_data_sock;
	return p_thesession->pasv_listen_fd=pasv_data_sock;
}

/*===============================================================================
function:将数据传输通道(IP和port)的转换为形式如:192,168,100,201,12,241的字符串
注:端口号计算:12*256+241
===============================================================================*/
char* myftp_make_data_addr_str(char* tmp_pasv_addr_string, pMYFTP_SESSION p_thesession)
{
	char data_trans_ip[16]={0};
	strcpy(data_trans_ip, (char*)inet_ntoa(p_thesession->local_addr.un.u_sockaddr_in.sin_addr));

	unsigned short port=ntohs(p_thesession->local_addr.un.u_sockaddr_in.sin_port);//必须将主机字节序转换为网络字节序,否则导致客户端在解释地址的时候出错,不能得到正确的端口
	unsigned short a1=port/256;
	unsigned short a2=port%256;
	sprintf(tmp_pasv_addr_string,"227 Entering Passive Mode (%s,%d,%d)\r\n", myftp_to_dotstr(data_trans_ip),a1,a2);
	return tmp_pasv_addr_string;
}

/*===============================================================================
function:PASV命令解释函数
备注:该模式编码过程--》创建一个新的数据连接套接字,将该套接字绑定到本机ip和一个随机端口号上
	然后开始监听客户端。具体的accept函数在各个数据传输命令中实现
===============================================================================*/
int myftp_cmd_handle_pasv(pMYFTP_SESSION p_thesession)
{
	if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
	{
		write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
		return -1;
	}
	p_thesession->is_port_or_pasv=PASV;
	int pasv_listen_sock=myftp_make_pasv_data_fd(p_thesession);

	char tmp_pasv_addr_string[100]={0};
	myftp_make_data_addr_str(tmp_pasv_addr_string,p_thesession);
	write(p_thesession->cmd_cntl_fd, tmp_pasv_addr_string,strlen(tmp_pasv_addr_string));

	if (-1==listen(pasv_listen_sock,LISTEN_BACKLOG))
	{
		die("myftp_cmd_handle_pasv listen error.\n");
	}
	return -1;
}

/*===============================================================================
function:文件传输类型命令TYPE解释函数
注:ASCII模式主要用于文本文件的传输;BINARY模式主要用于压缩文件、图像、音视频文件等的传输
===============================================================================*/
int myftp_cmd_handle_type(pMYFTP_SESSION p_thesession)
{
	if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
	{
		write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
		return -1;
	}
	if (strcmp(p_thesession->p_cmd_line->arg,"A")==0)
	{
		write(p_thesession->cmd_cntl_fd,"220 Switching to ASCII mode.\r\n",strlen("220 Switching to ASCII mode.\r\n"));
		strcpy(p_thesession->typemode,"ASCII");
	}
	else if (strcmp(p_thesession->p_cmd_line->arg,"I")==0)
	{
		write(p_thesession->cmd_cntl_fd,"220 Switching to Binary mode.\r\n",strlen("220 Switching to Binary mode.\r\n"));
		strcpy(p_thesession->typemode,"BINARY");
	}
	return -1;
}

/*===============================================================================
function:设置文件传输模式命令MODE解释函数。
注:默认为stream模式,其他模式未实现。
===============================================================================*/
int myftp_cmd_handle_mode(pMYFTP_SESSION p_thesession)
{
	if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
	{
		write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
		goto RETURN;
	}
	if (*p_thesession->p_cmd_line->arg == 'S'||*p_thesession->p_cmd_line->arg == 's')
	{
		write(p_thesession->cmd_cntl_fd, "200 Mode set to S.\r\n", strlen("200 Mode set to S.\r\n"));
	}
	else
	{
		write(p_thesession->cmd_cntl_fd, "504 Bad MODE command.\r\n", strlen("504 Bad MODE command.\r\n"));
	}
RETURN:
	return -1;
}

/*===============================================================================
function:打开服务器上客户需要下载的文件
===============================================================================*/
int myftp_get_downloadfile(pMYFTP_SESSION p_thesession)//打开要下载的文件。
{
	if (-1 == access(p_thesession->p_cmd_line->arg, F_OK))//if file not exist
	{
		return FALSE;
	}
	else
	{
		int download_file_fd=-1;
		if (-1 == (download_file_fd=open(p_thesession->p_cmd_line->arg, O_RDONLY)))
		{
			return FALSE;
		}
		return download_file_fd;
	}
}


static unsigned long retr_num;//文件域变量,专用于统计下载数据时的传输量的记录

/*===============================================================================
function:下载文件子进程发送的信号的处理函数。当数据传输子进程结束时,进入该函数,对传输的数据量进程统计
reamrk:信号处理函数内部不能有不可重入函数出现,比如标准输入输出函数等
===============================================================================*/
void myftp_retr_file_stat(int signo, siginfo_t *info, void *pthesession)
{
	retr_num += info->si_value.sival_int;
}

/*=================================================================================
function:下载文件命令RETR解释函数
注:其处理方式与其他数据传输命令类同,可参照之以理解
=================================================================================*/
int myftp_cmd_handle_retr(pMYFTP_SESSION p_thesession)
{
	if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
	{
		write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
		return -1;
	}
	struct sigaction sigact;
	sigact.sa_flags=SA_SIGINFO;
	sigact.sa_sigaction=myftp_retr_file_stat;
	static unsigned int file_num;
	int download_file_fd=myftp_get_downloadfile( p_thesession);
	if (sigaction(SIGUSR2, &sigact, NULL)==-1)
	{
		die("download set sig handler error\n");
	}

	int trans_file_fd=fork();
	switch (trans_file_fd)
	{
	case -1:
		return -1;
	case 0:
		if (!download_file_fd)
		{
			write(p_thesession->cmd_cntl_fd, "550 Failed to open file.\r\n", strlen("550 Failed to open file.\r\n"));
			exit(0);
		}
		else
		{
			struct stat sbuf;
			if (-1 == lstat(p_thesession->p_cmd_line->arg, &sbuf))
			{
				write(p_thesession->cmd_cntl_fd, "550 Failed to open file.\r\n",strlen("550 Failed to open file.\r\n"));
				exit(0);
			} 
			else
			{
				char tmp_filesize[128]={0};
				sprintf(tmp_filesize,"150 Opening BINARY mode data connection for %s (%d bytes).\r\n",p_thesession->p_cmd_line->arg, sbuf.st_size);
				write(p_thesession->cmd_cntl_fd, tmp_filesize,strlen(tmp_filesize));
				unsigned int read_count;
				static unsigned int read_sum;
				char data_buf[1024];
				int len=sizeof(data_buf);
				int tmp_data_fd=-1;
				lseek(download_file_fd, (long)p_thesession->transfile_start_pos, SEEK_SET);
				memset(data_buf, 0, len);
				if (p_thesession->is_port_or_pasv==PORT)
				{
					tmp_data_fd=p_thesession->port_data_fd;
				}
				else
				{
					MYFTP_SOCKADDR tmp_data_addr;
					int len=sizeof(MYFTP_SOCKADDR);
					p_thesession->pasv_data_fd=accept(p_thesession->pasv_listen_fd,&tmp_data_addr.un.u_sockaddr,&len);
					if(p_thesession->pasv_data_fd==-1)
					{
#ifdef __MYDEBUG
						perror("RETR accept client_id");
#endif
					}
					close(p_thesession->pasv_listen_fd);
					tmp_data_fd=p_thesession->pasv_data_fd;
				}
				unsigned long int i=0;
				while (read_count=read(download_file_fd, data_buf, len))
				{
					i++;
					if (!(i%myftp_conf.file_trans_rate))
						usleep(50*10000+50*10000-10*1000);//这个限速比较精确,浮动范围为-1%~+1%
					int write_count=write(tmp_data_fd, data_buf, read_count); 
					read_sum+=read_count;
					memset(data_buf, 0, len);
				}
				close(tmp_data_fd);
				write(p_thesession->cmd_cntl_fd, "226 File send OK.\r\n", strlen("226 File send OK.\r\n"));
				close(p_thesession->cmd_cntl_fd);
				sigval_t val;
				val.sival_int=read_sum;
				if(sigqueue(getppid(),SIGUSR2,val)==-1)
				{
					die("upload sigqueue error\n");
				}				
				exit(1);
			}
		}
		break;
		
	default:
		p_thesession->down_file_num=++file_num;
#ifdef __MYDEBUG
		printf("download file num %d\n",p_thesession->down_file_num);
		printf("farther proc download num %d bytes\n",p_thesession->size_of_retrfile);
#endif
		close(download_file_fd);
		p_thesession->data_trans_child_pid=trans_file_fd;
		close(p_thesession->port_data_fd);
		close(p_thesession->pasv_data_fd);
		return -1;
		} 
}

/*=======================================================================
function:创建客户要上传的文件
=======================================================================*/
int myftp_creat_transfile(pMYFTP_SESSION p_thesession)//客户在上传文件时,使用之 
{
	int stor_file=-1;
	if (-1 == access(p_thesession->p_cmd_line->arg, F_OK))//if file not exist
	{
		stor_file = open(p_thesession->p_cmd_line->arg, O_CREAT|O_EXCL|O_RDWR, USER_MASK);
	}
	else
	{
		stor_file = open(p_thesession->p_cmd_line->arg, O_RDWR, USER_MASK);
	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -