📄 myftp_state_machine.c
字号:
#include "myftp_state_machine.h"
#include "myftp_cmd_string.h"
#include "public.h"
CMD_HANDLE_STATE cmd_state_list[33]={
{"USER", myftp_cmd_handle_user},
{"PASS", myftp_cmd_handle_pass},
{"CWD", myftp_cmd_handle_cwd},
{"XCWD", myftp_cmd_handle_xcwd},
{"CDUP", myftp_cmd_handle_cdup},
{"QUIT", myftp_cmd_handle_quit},
{"REIN", myftp_cmd_handle_rein},
{"PORT", myftp_cmd_handle_port},
{"PASV", myftp_cmd_handle_pasv},
{"TYPE", myftp_cmd_handle_type},
{"MODE", myftp_cmd_handle_mode},
{"RETR", myftp_cmd_handle_retr},
{"STOR", myftp_cmd_handle_stor},
{"APPE", myftp_cmd_handle_appe},
{"REST", myftp_cmd_handle_rest},
{"RNFR", myftp_cmd_handle_rnfr},
{"RNTO", myftp_cmd_handle_rnto},
{"ABOR", myftp_cmd_handle_abor},
{"DELE", myftp_cmd_handle_dele},
{"RMD", myftp_cmd_handle_rmd},
{"XRMD", myftp_cmd_handle_xrmd},
{"PWD", myftp_cmd_handle_pwd},
{"XPWD", myftp_cmd_handle_xpwd},
{"MKD", myftp_cmd_handle_mkd},
{"XMKD", myftp_cmd_handle_xmkd},
{"LIST", myftp_cmd_handle_list},
{"NLST", myftp_cmd_handle_nlst},
{"SYST", myftp_cmd_handle_syst},
{"STAT", myftp_cmd_handle_stat},
{"SIZE", myftp_cmd_handle_size},
{"HELP", myftp_cmd_handle_help},
{"NOOP", myftp_cmd_handle_noop},
{NULL,NULL}
};
/*===============================================================================
function:标记文件类型及权限函数,形如:drwxrwxrwx
===============================================================================*/
void getprio(struct stat *ptr,char *pow)
{
mode_t modes;
modes = ptr->st_mode;
if(S_ISDIR(modes))
pow[0] = 'd';
else if(S_ISREG(modes))
pow[0] = '-';
else if(S_ISLNK(modes))
pow[0] = 'l';
else if(S_ISBLK(modes))
pow[0] = 'b';
else if(S_ISCHR(modes))
pow[0] = 'c';
else if(S_ISFIFO(modes))
pow[0] = 'p';
else if(S_ISSOCK(modes))
pow[0] = 's';
else
pow[0] = ' ';
if(modes & S_IRUSR)
pow[1] = 'r';
else
pow[1] = '-';
if(modes & S_IWUSR)
pow[2] = 'w';
else
pow[2] = '-';
if(modes & S_IXUSR)
pow[3] = 'x';
else
pow[3] = '-';
if(modes & S_IRGRP)
pow[4] = 'r';
else
pow[4] = '-';
if(modes & S_IWGRP)
pow[5] = 'w';
else
pow[5] = '-';
if(modes & S_IXGRP)
pow[6] = 'x';
else
pow[6] = '-';
if(modes & S_IROTH)
pow[7] = 'r';
else
pow[7] = '-';
if(modes & S_IWOTH)
pow[8] = 'w';
else
pow[8] = '-';
if(modes & S_IXOTH)
pow[9] = 'x';
else
pow[9] = '-';
pow[10]=0;
}
/*===============================================================================
function:port模式时,将端口字符串转换为数字
注:形如192,168,52,203,14,250将14和250转化为数字并依次存入num数组中
===============================================================================*/
void myftp_str_to_num(int num[], char* p_str)
{
int len=strlen(p_str);
int i=1;
for (;i>=0;i--,len--)
{
while(isdigit(*(p_str+len-1)))
{
len--;
}
num[i]=atoi(p_str+len);
*(p_str+len-1)='\0';
}
}
/*===============================================================================
function:将形如192,168,203,203的字符串转换为192.168.203.203类型的字符串
===============================================================================*/
void myftp_ip_to_dot_str(char* p_str)
{
int i=0;
while (*(p_str+i))
{
if (*(p_str+i) == ',')
{
*(p_str+i) = '.';
}
i++;
}
}
/*===============================================================================
function:将主动模式时,得到的客户的形如192,168,203,203,14,156的字符串
转换为IP地址及端口
注:端口计算公式 14*256+156
===============================================================================*/
void myftp_arg_to_addr(pMYFTP_SESSION p_thesession)
{
int num[2];
myftp_str_to_num(num, p_thesession->p_cmd_line->arg);
int tmp=(num[0]<<=8)+num[1];
myftp_ip_to_dot_str(p_thesession->p_cmd_line->arg);//inet_addr函数只认识点进十进制的ip字符串。所以这里还得以逗号隔开的ip串转换一下。
strcpy(p_thesession->remote_data_ip_str, p_thesession->p_cmd_line->arg);
p_thesession->remote_data_port=tmp;
p_thesession->data_sockaddr.un.u_sockaddr_in.sin_family=AF_INET;
p_thesession->data_sockaddr.un.u_sockaddr_in.sin_port=htons(tmp);
p_thesession->data_sockaddr.un.u_sockaddr_in.sin_addr.s_addr=inet_addr(p_thesession->p_cmd_line->arg);
}
/*===============================================================================
function:创建port模式时的数据连接套接字
===============================================================================*/
void myftp_make_port_data_socket(pMYFTP_SESSION p_thesession)
{
int tmp_data_sock=socket(AF_INET,SOCK_STREAM,0);
if (-1 == tmp_data_sock)
{
die("myftp_make_port_data_socket error.\n");
}
p_thesession->port_data_fd=tmp_data_sock;//主动模式数据连接的套节字。
}
/*===============================================================================
function:保存客户密码
===============================================================================*/
void myftp_pwdstr_cpy(pMYFTP_SESSION p_thesession)
{
strcpy(p_thesession->passwd_str,p_thesession->p_cmd_line->arg);
}
/*===============================================================================
function:用户密码字符串(都为加密后的字符串)验证
===============================================================================*/
int myftp_qualify(char* dst_pwd, char* src_pwd)
{
return !strcmp(dst_pwd,src_pwd);
}
/*===============================================================================
function:保存客户名
===============================================================================*/
void myftp_usrstr_cpy(pMYFTP_SESSION p_thesession)
{
strcpy(p_thesession->user_str,p_thesession->p_cmd_line->arg);
}
/*===============================================================================
function:MD5加密用户密码
===============================================================================*/
char* myftp_mycrypt(const char *passwd,const char *mysalt)
{
char *spwd=(char*)crypt(passwd,mysalt);//mysalt为密码暗纹的前12位
return spwd;
}
/*========================================================================
function:FTP 命令分析器函数入口
========================================================================*/
int myftp_state_machine_entry(pMYFTP_SESSION p_thesession)
{
int i=0,quit=-1;
while(cmd_state_list[i].cmd)
{
if (strcasecmp(p_thesession->p_cmd_line->cmd,cmd_state_list[i].cmd)==0)
{
quit = cmd_state_list[i].cmd_handler(p_thesession);
break;
}
else
i++;
}
if (!cmd_state_list[i].cmd)
{
write(p_thesession->cmd_cntl_fd,"500 Unknown command.\r\n",strlen("500 Unknown command.\r\n"));
}
return quit;
}
/*===============================================================================
function:USER命令处理函数
===============================================================================*/
int myftp_cmd_handle_user(pMYFTP_SESSION p_thesession)
{
if(*p_thesession->p_cmd_line->arg=='\n')//user name 不允许为空值
{
write(p_thesession->cmd_cntl_fd,"530 Bad command sequence.\r\n",strlen("530 Bad command sequence.\r\n"));
return -1;
}
if(strcmp(p_thesession->p_cmd_line->arg,"anonymous")==0)//如果是匿名用户登录
{
if (!myftp_conf.is_allowed_anoymous)//且不允许匿名登录
{
write(p_thesession->cmd_cntl_fd, "550 anonymous is not allowed.\r\n", strlen("550 anonymous is not allowed.\r\n"));
return -1;
}
}
if (strcmp(p_thesession->p_cmd_line->arg,"root")==0)//如果是root用户登录
{
if (!myftp_conf.is_allowed_root)//且不允许root登录
{
write(p_thesession->cmd_cntl_fd, "550 root is not allowed.\r\n", strlen("550 root is not allowed.\r\n"));
return -1;
}
}
myftp_usrstr_cpy(p_thesession);//将用户名存放起来
write(p_thesession->cmd_cntl_fd,"331 Please specify the password.\r\n",strlen("331 Please specify the password.\r\n"));
return -1;
}
/*===============================================================================
function:PASS命令处理函数
===============================================================================*/
int myftp_cmd_handle_pass(pMYFTP_SESSION p_thesession)
{
struct spwd* myspwd=0;
char mysalt[13]={0};
char *_spwd=0;
if (seteuid(0) == -1)//如果没有这句,则客户端在手动输入pass命令后,将导致语句:myspwd=getspnam(p_thesession->user_str);\
不能被执行。因为,此时运行此程序的用户的有效ID在之前已经被切换为非root用户了。
{
die("seteuid(0) error\n");
}
if (strcmp(p_thesession->user_str,"anonymous")==0)//如果是匿名登录
{
strcpy(p_thesession->user_str, "ftp");//用户就为ftp
p_thesession->is_anonymous=TRUE;
goto ANONYMOUS;
}
if (!(myspwd=getspnam(p_thesession->user_str)))//根据用户名查询,on failure, 0 return.
{
goto FAILED_TO_LOGIN;
}
myspwd=getspnam(p_thesession->user_str);
strncpy(mysalt,myspwd->sp_pwdp,12);//截取MD5密文的前12位,作为crypt函数的salt参数。
_spwd=myftp_mycrypt(p_thesession->p_cmd_line->arg,mysalt);//加密用户输入的密码
if(myftp_qualify(_spwd,myspwd->sp_pwdp))//验证用户密码是否正确
{
ANONYMOUS:
myftp_pwdstr_cpy(p_thesession);//将密码存放起
p_thesession->cur_usr_pwd=getpwnam(p_thesession->user_str);//登录成功后,将当前目录置为家目录,并且改变工作目录为用户家目录。
setegid(p_thesession->cur_usr_pwd->pw_gid);//设置有效组
seteuid(p_thesession->cur_usr_pwd->pw_uid);
chdir(p_thesession->cur_usr_pwd->pw_dir);
strcpy(p_thesession->cur_dir,p_thesession->cur_usr_pwd->pw_dir);
write(p_thesession->cmd_cntl_fd,"230 Login successful. Have fun.\r\n",strlen("230 Login successful. Have fun.\r\n"));
}
else
{
FAILED_TO_LOGIN:
bzero(p_thesession->user_str,USER_LEN);
write(p_thesession->cmd_cntl_fd,"530 Login incorrect.\r\n",strlen("530 Login incorrect.\r\n"));
}
return -1;
}
/*===============================================================================
function:CWD改变目录命令处理函数
注:如果是匿名登录,只允许其在目录/var/ftp/下操作。只需将所有的包含/var/ftp/字符串的目录
中的该字符串替换为/,客户端则会自动处理。也就是再不能切换到其它目录下了
===============================================================================*/
int myftp_cmd_handle_cwd(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;
}
char tmp_wd[MAX_PATH];
if (p_thesession->is_anonymous)//如果是匿名登录
{
sprintf(tmp_wd,"/var/ftp/%s/",p_thesession->p_cmd_line->arg);
if (!chdir(tmp_wd))
{
strcpy(p_thesession->cur_dir,tmp_wd);
write(p_thesession->cmd_cntl_fd, "250 Directory successfully changed.\r\n", strlen("250 Directory successfully changed.\r\n"));
}
else
write(p_thesession->cmd_cntl_fd, "550 Failed to change directory.\r\n", strlen("550 Failed to change directory.\r\n"));
return -1;
}
if (!chdir(p_thesession->p_cmd_line->arg))
{
sprintf(p_thesession->cur_dir,"%s/",getcwd(tmp_wd, MAX_PATH));
write(p_thesession->cmd_cntl_fd, "250 Directory successfully changed.\r\n", strlen("250 Directory successfully changed.\r\n"));
}
else
write(p_thesession->cmd_cntl_fd, "550 Failed to change directory.\r\n", strlen("550 Failed to change directory.\r\n"));
return -1;
}
/*===============================================================================
function:XCWD命令,与CWD命令处理相同
===============================================================================*/
int myftp_cmd_handle_xcwd(pMYFTP_SESSION p_thesession)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -