文章目录
1.目录列表数据的格式的传输
-
数据传输:
参照以下的方式:
-
eg:
ftpproto.h
#ifndef _FTP_PROTO_H_
#define _FTP_PROTO_H_
#include "session.h"
void handle_child(session_t *sess);
int list_common(void);
#endif /* _FTP_PROTO_H_ */
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);
int list_common(void);
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 },
{"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},
{"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 },
{"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);
}
*/
int i;
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;
}
}
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));
}
int list_common(void)
{
DIR *dir = opendir(".");//打开当前目录
if (dir == NULL)
{
return 0;
}
struct dirent *dt;
struct stat sbuf;
while ((dt = readdir(dir)) != NULL)//遍历目录中的文件,遍历到结束会返回NULL
{
//遍历到的文件的名称dt->d_name
//man 2 stat,lstat与stat区别是:如果符号链接,lstat可以看到,而stat看到的是被连接的源文件,链接文件看不到
if (lstat(dt->d_name, &sbuf) < 0)
{
continue;
}
if (dt->d_name[0] == '.')//过滤掉.号开头的文件,.tmp等等这样的文件
continue;
char perms[] = "----------";//定义权限位
perms[0] = '?';//文件类型
mode_t mode = sbuf.st_mode;
switch (mode & S_IFMT)//获取文件类型
{
case S_IFREG:
perms[0] = '-';
break;
case S_IFDIR:
perms[0] = 'd';
break;
case S_IFLNK:
perms[0] = 'l';
break;
case S_IFIFO:
perms[0] = 'p';
break;
case S_IFSOCK:
perms[0] = 's';
break;
case S_IFCHR:
perms[0] = 'c';
break;
case S_IFBLK:
perms[0] = 'b';
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';
}
//特殊权限位
//chmod 4744 file与chod 4644 file的区别是:前者的x位变为s,后者的x位变为S,因为前者有x,后者无
if (mode & S_ISUID)
{
perms[3] = (perms[3] == 'x') ? 's' : 'S';
}
if (mode & S_ISGID)
{
perms[6] = (perms[6] == 'x') ? 's' : 'S';
}
if (mode & S_ISVTX)
{
perms[9] = (perms[9] == 'x') ? 't' : 'T';
}
char buf[1024] = {0};
int off = 0;
off += sprintf(buf, "%s ", perms);//返回值为格式化到buf字符串中的长度
off += sprintf(buf + off, " %3d %-8d %-8d ", sbuf.st_nlink, sbuf.st_uid, sbuf.st_gid);//8d,表示有8位
off += sprintf(buf + off, "%8lu ", (unsigned long)sbuf.st_size);
const char *p_date_format = "%b %e %H:%M";//日期格式
struct timeval tv;
gettimeofday(&tv, NULL);//获取系统当前时间,NULL:表示时区,这里表示取当前系统的时区
time_t local_time = tv.tv_sec;//time_t就是秒
//(local_time - sbuf.st_mtime) > 60*60*24*182表示系统时间-文件时间后的值超过半年,单位s
if (sbuf.st_mtime > local_time || (local_time - sbuf.st_mtime) > 60*60*24*182)
{
//man strftime
//%b是月份的缩写,%d是月份中的某一天,%e与%d类似,只是如果是01的话会变成空格1
p_date_format = "%b %e %Y";
}
char datebuf[64] = {0};
struct tm* p_tm = localtime(&local_time);//localtime将秒转换为结构体struct tm
strftime(datebuf, sizeof(datebuf), p_date_format, p_tm);
off += sprintf(buf + off, "%s ", datebuf);
if (S_ISLNK(sbuf.st_mode))//man 2 stat,判断是否是符号链接文件
{
char tmp[1024] = {0};
readlink(dt->d_name, tmp, sizeof(tmp));//获取链接文件指向的文件,保存在tmp中
off += sprintf(buf + off, "%s -> %s\r\n", dt->d_name, tmp);
}
else
{
off += sprintf(buf + off, "%s\r\n", dt->d_name);
}
printf("%s", buf);
}
closedir(dir);
return 1;
}
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)
{
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);
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"
#include "ftpproto.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)
{
list_common();
exit(EXIT_SUCCESS);
// 字符串测试
/*
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;
}
其余文件没变
- 测试:
与ls -l对比
与vsftpd的方式对比