Linux网络编程综合运用之MiniFtp实现(八)
上节中实现了"USER"和"PASS"命令,如下:
事实上FTP是有很多命令组成的,如果就采用上面的这种方法来实现的话,就会有很多if...else if语句,代码显得很臃肿,所以有必要想办法来避免这种写法,所以一个新的方式既将诞生------命令映射,实际上在之前读取配置文件变量时就已经接触到了,下面则看具体的做法:
下面这个表是FTP命令的声明:
函数 |
说明 |
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); |
所以在程序中先声明:
记得当时实现配置模块时,一个配置项与配置项变量相对应,而这边则应该是一个命令字符串与一个命令处理函数相对应,因而也能定义一个结构体来配置这种对应关系,如下:
FTP命令与命令处理函数对应表 |
typedef struct ftpcmd { const char *cmd;//命令字符串 void (*cmd_handler)(session_t *sess);//函数指针 } ftpcmd_t; static ftpcmd_t ctrl_cmds[] = { /* 访问控制命令 */ {"USER", do_user },//如果是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 } }; |
另外发现,之前配置模块的最后是NULL结尾的,而这里并没有,所以这次是用另外一种遍历方法来判断是否遍历完,下面先将上面的映射关系代码拷至代码中:
ftpproto.c:
#include "ftpproto.h"
#include "sysutil.h"
#include "str.h"
#include "ftpcodes.h" static void ftp_reply(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 },
{"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)
{
writen(sess->ctrl_fd, "220 (miniftpd 0.1)\r\n", strlen("220 (miniftpd 0.1)\r\n"));
int ret;
while ()
{
memset(sess->cmdline, , sizeof(sess->cmdline));
memset(sess->cmd, , sizeof(sess->cmd));
memset(sess->arg, , sizeof(sess->arg));
ret = readline(sess->ctrl_fd, sess->cmdline, MAX_COMMAND_LINE);
if (ret == -)
ERR_EXIT("readline");
else if (ret == )
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) == )
{
do_user(sess);
}
else if (strcmp("PASS", sess->cmd) == )
{
do_pass(sess);
}
}
} 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) != )
{
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 ftp_reply(session_t *sess, int status, const char *text)
{
char buf[] = {};
sprintf(buf, "%d %s\r\n", status, text);
writen(sess->ctrl_fd, buf, strlen(buf));
}
而像之前的配置映射的方式代码写法如下:
但是由于这次并没有以NULL结尾,所以说得换一种新的方式,如下:
编译一下:
所以需要实现这些函数:
#include "ftpproto.h"
#include "sysutil.h"
#include "str.h"
#include "ftpcodes.h" static void ftp_reply(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 },
{"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)
{
writen(sess->ctrl_fd, "220 (miniftpd 0.1)\r\n", strlen("220 (miniftpd 0.1)\r\n"));
int ret;
while ()
{
memset(sess->cmdline, , sizeof(sess->cmdline));
memset(sess->cmd, , sizeof(sess->cmd));
memset(sess->arg, , sizeof(sess->arg));
ret = readline(sess->ctrl_fd, sess->cmdline, MAX_COMMAND_LINE);
if (ret == -)
ERR_EXIT("readline");
else if (ret == )
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[]);
for (i=; i<size; i++)
{
if (strcmp(ctrl_cmds[i].cmd, sess->cmd) == )
{
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.");
}
}
} static void ftp_reply(session_t *sess, int status, const char *text)
{
char buf[] = {};
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) != )
{
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)
{
} 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)
{
}
再次编译运行:
可见,命令解析一切OK,接下来就一个个命令来实现,首先是"SYST"命令,对照着vsftpd来做:
所以对应的函数中给出如下输出:
编译运行:
其中"FEAT"表示Feature,表示服务端的特性,实际上这条命令不实现也没有关系,如果暂且不想实现它,则可以给它对应的处理函数配置成NULL,如下:
编译运行:
紧着着发送了CLNT命令,这个命令没有,所以就提示无效的命令:
【注意】:当无效的命令时,我们也必须给它响应,否则客户端就会阻塞。
当响应了CLNT命令之后,最后发送了REST 100命令,这表示断点续传,对比着vsftpd输出结果来看:
所以,我们也来先实现FEAT命令,实现完之后,看是否最后还会发送RESET 100命令,将FEAT的命令注释还原:
然后处理它对应的函数:
然后接vsftpd的响应格式来输出:
再次编译运行:
接着来实现PWD命令,先来看下vsftpd这个命令是如何响应的:
下面来实现PWD命令:
【说明】:
编译运行:
先看一下vsftpd中的"TYPE A"的响应:
如果是输入其它的TYPE命令呢?可以按如下步骤输出:
而如果输入"TYPE I"呢?
所以则照着这几种情况实现TYPE命令:
另外这个状态需要记录在session当中,之后会用到,所以需要增加一个变量至session结构体中:
修改它的初始化:
在do_type函数中来进行记录:
【说明】:Ascii和二进制协议进行传输的区别就在于是否要处理"\r\n",这个在之前FTP协议时有说明过。
编译运行:
接下来就要进行列表的传输了,在传输时需要创建数据连接通道,在创建通道之前是需要协商使用PORT模式还是PASV模式,所以这里先发送PASV模式出来了,这个下次来实现,先学到这~
Linux网络编程综合运用之MiniFtp实现(八)的更多相关文章
- Linux网络编程综合运用之MiniFtp实现(一)
春节过后,万物复苏,在这元宵佳节的前一天,决定继续开启新年的学习计划,生命在于运动,提高源于学习,在经过漫长的Linux网络编程学习后,接下来会以一个综合的小项目来将所学的知识点综合运用,首先是对项目 ...
- Linux网络编程综合运用之MiniFtp实现(四)
从今天开始,正式进入MiniFtp的代码编写阶段了,好兴奋,接下来很长一段时间会将整个实现过程从无到有一点点实现出来,达到综合应用的效果,话不多说正入正题: 这节主要是将基础代码框架搭建好,基于上节介 ...
- Linux网络编程综合运用之MiniFtp实现(九)
上次中实现了FTP命令的映射来避免很多if....else的判断,这次主要是开始实现目录列表的传输,先看一下目前实现的: 数据连接创建好之后则开始进行目录列表的传输了,而要传输目录列表,首先要将目录列 ...
- Linux网络编程综合运用之MiniFtp实现(五)
转眼兴奋的五一小长假就要到来了,在放假前夕还是需要保持一颗淡定的心,上次中已经对miniFTP有基础框架进行了搭建,这次继续进行往上加代码,这次主要还是将经历投射到handle_child()服务进程 ...
- Linux网络编程综合运用之MiniFtp实现(七)
上节中实现了配置文件的解析,这节来实现用户登录的验证,首先用客户端来登录vsftpd来演示登录的过程: 接着再连接miniftpd,来看下目前的效果: 接下来实现它,与协议相关的模块都是在ftppro ...
- Linux网络编程综合运用之MiniFtp实现(六)
间隔了一周时间没写了,由于今年的股势行情貌似不错的样子,对于对股市完全不懂的我也在蠢蠢欲动,所以最近一周业余时间在“不务正业”-----学习炒股.发现学习它其实挺费神的,满脑子都是走势图,而且是神经有 ...
- Linux网络编程综合运用之MiniFtp实现(三)
前面已经对FTP相关的一些概念有了基本的认识,接下来就要进入代码编写阶段了,也是非常兴奋的阶段,在开启这个它之前先对项目需求进行一个梳理,对其我们要实现的FTP服务器是一个什么样子. ftp命令列表 ...
- 【深入浅出Linux网络编程】 "开篇 -- 知其然,知其所以然"
[深入浅出Linux网络编程]是一个连载博客,内容源于本人的工作经验,旨在给读者提供靠谱高效的学习途径,不必在零散的互联网资源中浪费精力,快速的掌握Linux网络编程. 连载包含4篇,会陆续编写发出, ...
- 【linux草鞋应用编程系列】_5_ Linux网络编程
一.网络通信简介 第一部分内容,暂时没法描述,内容实在太多,待后续专门的系列文章. 二.linux网络通信 在linux中继承了Unix下“一切皆文件”的思想, 在linux中要实现网 ...
随机推荐
- C# 文档注释规范
C# 提供一种机制,使程序员可以使用含有 XML 文本的特殊注释语法为他们的代码编写文档.在源代码文件中,具有某种格式的注释可用于指导某个工具根据这些注释和它们后面的源代码元素生成 XML.使用这类语 ...
- 如何优雅的处理 async/await 异常
参考链接:https://cloud.tencent.com/developer/article/1470715 参考链接:https://www.jianshu.com/p/2935c0330dd2
- <automate the boring stuff with python> 正则强口令实例
书中7.18的强口令实践题 写一个函数,它使用正则表达式,确保传入的口令字符串是强口令.强口令的定义是: 长度不少于8 个字符,同时包含大写和小写字符,至少有一位数字. 你可能需要用多个正则表达式来测 ...
- 因修改/etc/sudoers权限导致sudo和su不能使用的解决方法(转)
转自: 因修改/etc/sudoers权限导致sudo和su不能使用的解决方法 系统环境:ubuntu 12.04 状况: 因为修改了/etc/sudoers以及相关权限,导致sudo无法使用,恰 ...
- LeetCode 189. 旋转数组(Rotate Array)
189. 旋转数组 LeetCode189. Rotate Array 题目描述 给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: [1,2,3,4,5,6, ...
- C++ 用 vector 生成三维数组,并计算行、列、高
//Microsoft Visual Studio 2015 Enterprise //用vector生成三维数组,并计算行.列.高 #include <iostream> #includ ...
- Python10之列表1(创建列表和列表添加元素的方法)
一.创建列表 列表名 = [’元素一‘,’元素二‘,’元素三‘,’元素四‘] 列表中的元素可以是任何类型,甚至可以是一个列表. list1 = ['詹姆斯','戴维斯','保罗乔治','字母哥',in ...
- WUSTOJ 1320: 饭卡(Java)动态规划-背包
题目链接:
- Shiro身份认证、盐加密
目的: Shiro认证 盐加密工具类 Shiro认证 1.导入pom依赖 <dependency> <groupId>org.apache.shiro</groupId& ...
- Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2) (1208F,1208G,1208H)
1208 F 大意: 给定序列$a$, 求$\text{$a_i$|$a_j$&$a_k$}(i<j<k)$的最大值 枚举$i$, 从高位到低位贪心, 那么问题就转化为给定$x$ ...