zoukankan      html  css  js  c++  java
  • 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   },

           {"377364377362ABOR", 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    },
        {"377364377362ABOR", 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)
    ", strlen("220 (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]
    ", sess->cmdline);
            // 去除
    
            str_trim_crlf(sess->cmdline);
            printf("cmdline=[%s]
    ", sess->cmdline);
            // 解析FTP命令与参数
            str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
            printf("cmd=[%s] arg=[%s]
    ", 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);
            }
        }
    }
    
    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]
    ", 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 ftp_reply(session_t *sess, int status, const char *text)
    {
        char buf[1024] = {0};
        sprintf(buf, "%d %s
    ", 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    },
        {"377364377362ABOR", 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)
    ", strlen("220 (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]
    ", sess->cmdline);
            // 去除
    
            str_trim_crlf(sess->cmdline);
            printf("cmdline=[%s]
    ", sess->cmdline);
            // 解析FTP命令与参数
            str_split(sess->cmdline, sess->cmd, sess->arg, ' ');
            printf("cmd=[%s] arg=[%s]
    ", 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.");
            }
        }
    }
    
    static void ftp_reply(session_t *sess, int status, const char *text)
    {
        char buf[1024] = {0};
        sprintf(buf, "%d %s
    ", 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]
    ", 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)
    {
    }
    
    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和二进制协议进行传输的区别就在于是否要处理" ",这个在之前FTP协议时有说明过。

    编译运行:

    接下来就要进行列表的传输了,在传输时需要创建数据连接通道,在创建通道之前是需要协商使用PORT模式还是PASV模式,所以这里先发送PASV模式出来了,这个下次来实现,先学到这~

  • 相关阅读:
    Python中的装饰器之@wraps(四)
    python中装饰器之有参装饰器(三)
    python中装饰器之叠加装饰器(二)
    python中装饰器之闭包函数(一)
    python中的命名空间
    函数的嵌套与迭代
    函数对象
    matlab学习checkbox使用
    matlab学习滚动条改变文本数值
    matlab学习GUI可调的界面窗口
  • 原文地址:https://www.cnblogs.com/webor2006/p/4605127.html
Copyright © 2011-2022 走看看