文章目录
1.ftp命令映射的实现
- eg:FTP命令与命令处理函数对应表,见:ftpproto.c
已完成的命令:SYST,EFAT,CLNT(没实现),PWD,TYPE A
ftpproto.c
#include "ftpproto.h"
#include "sysutil.h"
#include "str.h"
#include "ftpcodes.h"
void ftp_reply(session_t *sess, int status, const char *text);
void ftp_lreply(session_t *sess, int status, const char *text);
static void do_user(session_t *sess);
static void do_pass(session_t *sess);
static void do_cwd(session_t *sess);
static void do_cdup(session_t *sess);
static void do_quit(session_t *sess);
static void do_port(session_t *sess);
static void do_pasv(session_t *sess);
static void do_type(session_t *sess);
static void do_stru(session_t *sess);
static void do_mode(session_t *sess);
static void do_retr(session_t *sess);
static void do_stor(session_t *sess);
static void do_appe(session_t *sess);
static void do_list(session_t *sess);
static void do_nlst(session_t *sess);
static void do_rest(session_t *sess);
static void do_abor(session_t *sess);
static void do_pwd(session_t *sess);
static void do_mkd(session_t *sess);
static void do_rmd(session_t *sess);
static void do_dele(session_t *sess);
static void do_rnfr(session_t *sess);
static void do_rnto(session_t *sess);
static void do_site(session_t *sess);
static void do_syst(session_t *sess);
static void do_feat(session_t *sess);
static void do_size(session_t *sess);
static void do_stat(session_t *sess);
static void do_noop(session_t *sess);
static void do_help(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 },
{"CWD", do_cwd },
{"XCWD", do_cwd },
{"CDUP", do_cdup },
{"XCUP", do_cdup },
{"QUIT", do_quit },
{"ACCT", NULL },//NULL当前认识的命令,但是没有实现它,所以先填写一个NULL指针
{"SMNT", NULL },
{"REIN", NULL },
/* 传输参数命令 */
{"PORT", do_port },
{"PASV", do_pasv },
{"TYPE", do_type },
{"STRU", do_stru },
{"MODE", do_mode },
/* 服务命令 */
{"RETR", do_retr },
{"STOR", do_stor },
{"APPE", do_appe },
{"LIST", do_list },
{"NLST", do_nlst },
{"REST", do_rest },
{"ABOR", do_abor },
{"\377\364\377\362ABOR", do_abor},//实际上也是ABORT命令
{"PWD", do_pwd },
{"XPWD", do_pwd },
{"MKD", do_mkd },
{"XMKD", do_mkd },
{"RMD", do_rmd },
{"XRMD", do_rmd },
{"DELE", do_dele },
{"RNFR", do_rnfr },
{"RNTO", do_rnto },
{"SITE", do_site },
{"SYST", do_syst },
{"FEAT", do_feat },
{"SIZE", do_size },
{"STAT", do_stat },
{"NOOP", do_noop },
{"HELP", do_help },
{"STOU", NULL },//没有以{NULL, NULL}空表结尾,而这里以另外一种方式判断结束
{"ALLO", NULL }
};
void handle_child(session_t *sess)
{
ftp_reply(sess, FTP_GREET, "(miniftpd 0.1)");
int ret;
while (1)
{
memset(sess->cmdline, 0, sizeof(sess->cmdline));
memset(sess->cmd, 0, sizeof(sess->cmd));
memset(sess->arg, 0, sizeof(sess->arg));
ret = readline(sess->ctrl_fd, sess->cmdline, MAX_COMMAND_LINE);
if (ret == -1)
ERR_EXIT("readline");
else if (ret == 0)
exit(EXIT_SUCCESS);
printf("cmdline=[%s]\n", sess->cmdline);
// 去除\r\n
str_trim_crlf(sess->cmdline);
printf("cmdline=[%s]\n", sess->cmdline);
// 解析FTP命令与参数
str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
printf("cmd=[%s] arg=[%s]\n", sess->cmd, sess->arg);
// 将命令转换为大写
str_upper(sess->cmd);
// 处理FTP命令
/*
if (strcmp("USER", sess->cmd) == 0)
{
do_user(sess);
}
else if (strcmp("PASS", sess->cmd) == 0)
{
do_pass(sess);
}
*/
/*如果ctrl_cmds以{NULL, NULL}空表结尾,处理方式如下:
int i = 0;
while (ctrl_cmds[i].cmd != NULL)
{
if (strcmp(ctrl_cmds[i].cmd, sess->cmd) == 0)
break;
i++;
}
*/
//使用命令映射的方式,减少了很多if else语句,而只需要在ctrl_cmds表中配置即可
int i;
//sizeof(ctrl_cmds):该数组变量的大小,不等于数组的长度,指的是数组占用内存空间的大小
//数组当中第一个条目的大小:sizeof(ctrl_cmds[0]),就是一个结构体的大小
int size = sizeof(ctrl_cmds) / sizeof(ctrl_cmds[0]);//计算数组的长度
for (i=0; i<size; i++)
{
if (strcmp(ctrl_cmds[i].cmd, sess->cmd) == 0)
{
if (ctrl_cmds[i].cmd_handler != NULL)
{
ctrl_cmds[i].cmd_handler(sess);
}
else
{
ftp_reply(sess, FTP_COMMANDNOTIMPL, "Unimplement command.");
}
break;//break,i肯定小于size
}
}
if (i == size)//遍历结束,还没有找到命令
{
ftp_reply(sess, FTP_BADCMD, "Unknown command.");
}
}
}
void ftp_reply(session_t *sess, int status, const char *text)
{
char buf[1024] = {0};
sprintf(buf, "%d %s\r\n", status, text);
writen(sess->ctrl_fd, buf, strlen(buf));
}
void ftp_lreply(session_t *sess, int status, const char *text)
{
char buf[1024] = {0};
sprintf(buf, "%d-%s\r\n", status, text);
writen(sess->ctrl_fd, buf, strlen(buf));
}
static void do_user(session_t *sess)
{
//USER jjl
struct passwd *pw = getpwnam(sess->arg);
if (pw == NULL)
{
// 用户不存在
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
sess->uid = pw->pw_uid;
ftp_reply(sess, FTP_GIVEPWORD, "Please specify the password.");
}
static void do_pass(session_t *sess)
{
// PASS 123456
struct passwd *pw = getpwuid(sess->uid);
if (pw == NULL)
{
// 用户不存在
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
printf("name=[%s]\n", pw->pw_name);
struct spwd *sp = getspnam(pw->pw_name);
if (sp == NULL)
{
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
// 将明文进行加密
char *encrypted_pass = crypt(sess->arg, sp->sp_pwdp);
// 验证密码
if (strcmp(encrypted_pass, sp->sp_pwdp) != 0)
{
ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
return;
}
setegid(pw->pw_gid);
seteuid(pw->pw_uid);
chdir(pw->pw_dir);
ftp_reply(sess, FTP_LOGINOK, "Login successful.");
}
static void do_cwd(session_t *sess)
{
}
static void do_cdup(session_t *sess)
{
}
static void do_quit(session_t *sess)
{
}
static void do_port(session_t *sess)
{
}
static void do_pasv(session_t *sess)
{
}
static void do_type(session_t *sess)
{
//ASCII模式与Binary二进制码模式的区别是:是否对\r\n进行处理
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_stru(session_t *sess)
{
}
static void do_mode(session_t *sess)
{
}
static void do_retr(session_t *sess)
{
}
static void do_stor(session_t *sess)
{
}
static void do_appe(session_t *sess)
{
}
static void do_list(session_t *sess)
{
}
static void do_nlst(session_t *sess)
{
}
static void do_rest(session_t *sess)
{
}
static void do_abor(session_t *sess)
{
}
static void do_pwd(session_t *sess)
{
char text[1024] = {0};
char dir[1024+1] = {0};
getcwd(dir, 1024);//获取当前路径,man getcwd
sprintf(text, "\"%s\"", dir);//注意需要转义
ftp_reply(sess, FTP_PWDOK, text);
}
static void do_mkd(session_t *sess)
{
}
static void do_rmd(session_t *sess)
{
}
static void do_dele(session_t *sess)
{
}
static void do_rnfr(session_t *sess)
{
}
static void do_rnto(session_t *sess)
{
}
static void do_site(session_t *sess)
{
}
static void do_syst(session_t *sess)
{
ftp_reply(sess, FTP_SYSTOK, "UNIX Type: L8");
}
static void do_feat(session_t *sess)
{
ftp_lreply(sess, FTP_FEAT, "Features:");
writen(sess->ctrl_fd, " EPRT\r\n", strlen(" EPRT\r\n"));
writen(sess->ctrl_fd, " EPSV\r\n", strlen(" EPSV"));
writen(sess->ctrl_fd, " MDTM\r\n", strlen(" MDTM\r\n"));
writen(sess->ctrl_fd, " PASV\r\n", strlen(" PASV\r\n"));
writen(sess->ctrl_fd, " REST STREAM\r\n", strlen(" REST STREAM\r\n"));
writen(sess->ctrl_fd, " SIZE\r\n", strlen(" SIZE\r\n"));
writen(sess->ctrl_fd, " TVFS\r\n", strlen(" TVFS\r\n"));
writen(sess->ctrl_fd, " UTF8\r\n", strlen(" UTF8\r\n"));
ftp_reply(sess, FTP_FEAT, "End");
}
static void do_size(session_t *sess)
{
}
static void do_stat(session_t *sess)
{
}
static void do_noop(session_t *sess)
{
}
static void do_help(session_t *sess)
{
}
main.c
#include "common.h"
#include "sysutil.h"
#include "session.h"
#include "str.h"
#include "tunable.h"
#include "parseconf.h"
/*
typedef struct session
{
// 控制连接
char cmdline[MAX_COMMAND_LINE];
char cmd[MAX_COMMAND];
char arg[MAX_ARG];
// 父子进程通道
int parent_fd;
int child_fd;
} session_t;
*/
int main(void)
{
// 字符串测试
/*
char *str1 = " a b";
char *str2 = " ";
if (str_all_space(str1))
printf("str1 all space\n");
else
printf("str1 not all space\n");
if (str_all_space(str2))
printf("str2 all space\n");
else
printf("str2 not all space\n");
//char *str3 = "abcDef"; // 指针指向一个字符串常量,常量不能被修改
char str3[] = "abcDef";
str_upper(str3);
printf("str3=%s\n", str3);
long long result = str_to_longlong("12345678901234");
printf("result = %lld\n", result);
int n = str_octal_to_uint("711");
printf("n=%d\n", n);
*/
parseconf_load_file(MINIFTP_CONF);
printf("tunable_pasv_enable=%d\n", tunable_pasv_enable);
printf("tunable_port_enable=%d\n", tunable_port_enable);
printf("tunable_listen_port=%u\n", tunable_listen_port);
printf("tunable_max_clients=%u\n", tunable_max_clients);
printf("tunable_max_per_ip=%u\n", tunable_max_per_ip);
printf("tunable_accept_timeout=%u\n", tunable_accept_timeout);
printf("tunable_connect_timeout=%u\n", tunable_connect_timeout);
printf("tunable_idle_session_timeout=%u\n", tunable_idle_session_timeout);
printf("tunable_data_connection_timeout=%u\n", tunable_data_connection_timeout);
printf("tunable_local_umask=0%o\n", tunable_local_umask);
printf("tunable_upload_max_rate=%u\n", tunable_upload_max_rate);
printf("tunable_download_max_rate=%u\n", tunable_download_max_rate);
if (tunable_listen_address == NULL)
printf("tunable_listen_address=NULL\n");
else
printf("tunable_listen_address=%s\n", tunable_listen_address);
if (getuid() != 0)
{
fprintf(stderr, "miniftpd: must be started as root\n");
exit(EXIT_FAILURE);
}
/*
typedef struct session
{
// 控制连接
uid_t uid;
int ctrl_fd;
char cmdline[MAX_COMMAND_LINE];
char cmd[MAX_COMMAND];
char arg[MAX_ARG];
// 父子进程通道
int parent_fd;
int child_fd;
// FTP协议状态
int is_ascii;
} session_t;
*/
session_t sess =
{
/* 控制连接 */
0, -1, "", "", "",
/* 父子进程通道 */
-1, -1,
/* FTP协议状态 */
0
};
int listenfd = tcp_server(tunable_listen_address, tunable_listen_port);
int conn;
pid_t pid;
while (1)
{
conn = accept_timeout(listenfd, NULL, 0);
if (conn == -1)
ERR_EXIT("accept_tinmeout");
pid = fork();
if (pid == -1)
ERR_EXIT("fork");
if (pid == 0)
{
close(listenfd);
sess.ctrl_fd = conn;
begin_session(&sess);
}
else
close(conn);
}
return 0;
}
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];
// 父子进程通道
int parent_fd;
int child_fd;
// FTP协议状态:是否是ASCII模式
int is_ascii;
} session_t;
void begin_session(session_t *sess);
#endif /* _SESSION_H_ */
其余文件没变
common.h
ftpcodes.h
Makefile
miniftpd.conf
parseconf.c
parseconf.h
session.c
str.c
str.h
sysutil.c
sysutil.h
tunable.c
tunable.h
- 测试:实际上是按照客户端发来的命令,参照vsftpd服务器端的回复方式,来写miniftpd接收到这几个指令而采用的回复方式。所以下面的测试既可以当作结果,也可以当作是为啥要先写这几个命令的回复逻辑