前言

前一篇文章 vsftpd 登录过程的调试, 我们可以看到 因为 vsftpd 的多进程模型, 所以 我们无法在 clion 中直接调试相关的所有的处理逻辑 

所以 我再 vsftpd 登录过程的调试 里面的调试方法 主要是由两种输出的方式, 以便于能够拿到运行时的数据 

但是 在最近看了文章 记vsftpd虚拟用户登录返回530 Login incorrect解决过程 之后, 突然 想起了, 还有一个 gdb, 或者 lldb 也行 

呵呵 居然吧这个大宝贝给忘记了, 呵呵 本文会大致的介绍一下 我这边的 vsftpd 的调试的一些方式 

当然 我再调试 vsftpd 的部分代码的时候 也碰到了很多的问题, 也走了一些弯路, 这里也会 稍微介绍一下 

 

 

碰到的问题

编译的时候碰到的问题 

这部分的问题 可以直接参见 vsftpd 的调试环境的搭建 

 

printf 的输出

另外就是 编译好了之后, 的调试, 最开始 我是想直接 printf 输出 

在 vsf_standalone_main 里面的这部分输出是正常的 

但是 似乎是只要在一部分的代码里面 添加了 printf, 当客户端 open 192.168.202.133 的时候 都会报错? 报的错误大致是 "421 Service not available, remote server has closed connection"

原因 似乎是因为 当时加的 printf 里面的内容 不知道咋的 返回给客户端了吧, 然后客户端各种解析异常 

呵呵 但是在 mac 上面的虚拟机来复现, 似乎是 复现不了 

 

vsf_two_process_start fork 的子进程 输出报错 

这个子进程 不管你是加了什么普通的 printf 或者是 输出日志到文件的输出, 都是一个 报错, 呵呵 总之是很奇怪的 

ftp> open 192.168.202.133
Connected to 192.168.202.133.
500 OOPS: child died

我目前的处理方式是通过 已有的 bug(), die() 等方法来进行调试, 虽然 函数调用了之后 进程就没了 但是 好歹能够拿到运行时的数据[这个数据会 反馈给 客户端]

 

 

基于"输出"的调试

这个就是基于日志文件的输出了, 呵呵 

核心相关的代码 大致是如下, 不过 似乎是 fork 进程的时候, 拿到的 pid 似乎是有问题阿 

并且 代码中也有一些 不够严谨的地方, 就好比 formatStr 中 result 长度暂定的是 1024 等等, 不过 用于一些常规的调试 是没有问题的 

void println(const char *p_text) {
    FILE *fp = fopen("./vsftpd.log", "a+");
    if (fp == NULL) {
        printf(" open file error ");
        return;
    }

    fprintf(fp, "%s\n", p_text);
    fclose(fp);
}

void println2(const char *text1, const char *text2) {
    char *processPid = getPidStr();
    char *p_text = formatStr("%s - %s%s", processPid, text1, text2);
    println(p_text);
}

void println3(const char *text1, const char *text2, const char *text3) {
    char *processPid = getPidStr();
    char *p_text = formatStr("%s - %s%s%s", processPid, text1, text2, text3);
    println(p_text);
}

void printff(const char *p_text) {
    printf(p_text);
}

int getPid() {
    return (int) getpid();
}

char *getPidStr() {
    int pid = getPid();
    return itoaStr(pid, 10);
}


/**
 * 格式化给定的字符串
 *
 * @param format  format
 * @param ...
 * @return
 */
char *formatStr(char *fmt, ...) {
    char *result = malloc(1024);
    va_list ap;
    va_start(ap, fmt);
    vsprintf(result, fmt, ap);
    va_end(ap);
    return result;
}

/**
 * 给定的 str 的字节数
 *
 * @param str  str
 * @return
 */
int lengthOf(const char *str) {
    int i = 0;
    while (*(str + i) != '\0') {
        i++;
    }
    return i;
}


/**
 * itoaStr
 *
 * @param num
 * @param radix
 * @return
 */
char *itoaStr(int num, int radix) {
    char *result = malloc(256);
    itoa(num, result, radix);
    return result;
}

/**
 * itoa
 *
 * @param num num
 * @param str str
 * @param radix radix
 * @return
 */
char *itoa(int num, char *str, int radix) {
    /*索引表*/
    char index[] = "0123456789ABCDEF";
    unsigned unum;/*中间变量*/
    int i = 0, j, k;
    /*确定unum的值*/
    if (radix == 10 && num < 0)/*十进制负数*/
    {
        unum = (unsigned) -num;
        str[i++] = '-';
    } else unum = (unsigned) num;/*其他情况*/
    /*转换*/
    do {
        str[i++] = index[unum % (unsigned) radix];
        unum /= radix;
    } while (unum);
    str[i] = '\0';
    /*逆序*/
    if (str[0] == '-')
        k = 1;/*十进制负数*/
    else
        k = 0;

    for (j = k; j <= (i - 1) / 2; j++) {
        char temp;
        temp = str[j];
        str[j] = str[i - 1 + k - j];
        str[i - 1 + k - j] = temp;
    }
    return str;
}

 

 

基于 gdb 的调试 

当然 lldb 也行, 不过我这里的 虚拟机里面 自动就有 gdb, 因此 就直接使用现成的 gdb 了, 呵呵 

我们这里就拿 vsf_two_process_start fork 出来的子进程开刀, 呵呵 这个奇葩进程 

 首先 vsf_two_process_start 添加一个 sleep, 方便我们有 操作的时间 

04 vsftpd 的调试_5e

 

我在代码中嵌入的日志输出如下 

46798 - 192.168.202.1 <- host & port -> 49572
46798 - 46798 - vsf_standalone_main fork with newPid - 46814
46798 - 46798 - vsf_standalone_main fork with newPid - 0
46798 -  vsf_two_process_start, forked with newPid = 2
46798 -  vsf_two_process_start, forked with newPid = 0

vsftpd 相关的三个进程如下  

root@ubuntu:~# ps -ef | grep ftp
root      46798  46796  0 02:47 pts/19   00:00:00 /root/ClionWorkStations/vsftpd/vsftpd ./vsftpd.conf
root      46814  46798  0 02:50 ?        00:00:00 /root/ClionWorkStations/vsftpd/vsftpd ./vsftpd.conf
root      46816  46814  0 02:50 ?        00:00:00 /root/ClionWorkStations/vsftpd/vsftpd ./vsftpd.conf

 

gdb 相关调试信息如下 

呵呵 这样就能够拿到运行时的一些数据的情况了, 并且也可以 优雅的处理调试的相关操作 

root@ubuntu:~# gdb attach 46816
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1

warning: Target and debugger are in different PID namespaces; thread lists and other data are likely unreliable
0x00007f05ef7fb370 in __nanosleep_nocancel ()
    at ../sysdeps/unix/syscall-template.S:84
84	../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0  0x00007f05ef7fb370 in __nanosleep_nocancel ()
    at ../sysdeps/unix/syscall-template.S:84
#1  0x00007f05ef7fb2da in __sleep (seconds=0) at ../sysdeps/posix/sleep.c:55
#2  0x000055e160cefe60 in sleepInSecs (secs=60) at utility.c:100
#3  0x000055e160cfc5d6 in vsf_two_process_start (p_sess=0x7ffec487de40)
    at twoprocess.c:139
#4  0x000055e160cef82d in main (argc=2, argv=0x7ffec487e118) at main.c:254
(gdb) bt 
#0  vsf_two_process_start (p_sess=0x7ffec487de40) at twoprocess.c:146
#1  0x000055e160cef82d in main (argc=2, argv=0x7ffec487e118) at main.c:254
(gdb) list
141	  seccomp_sandbox_init();
142	  seccomp_sandbox_setup_prelogin(p_sess);
143	  seccomp_sandbox_lockdown();
144	//  println(" before init_connection ");
145	//  printff(" before init_connection ");
146	  init_connection(p_sess);
147	  /* NOTREACHED */
148	}
149	
150	static void
(gdb) break twoprocess.c:146
Breakpoint 1 at 0x55e160cfc5fb: file twoprocess.c, line 146.
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x000055e160cfc5fb in vsf_two_process_start 
                                                   at twoprocess.c:146
(gdb) c
Continuing.

Breakpoint 1, vsf_two_process_start (p_sess=0x7ffec487de40) at twoprocess.c:146
146	  init_connection(p_sess);
(gdb) print p_sess->p_local_addr->u            
$2 = {u_sockaddr = {sa_family = 2, 
    sa_data = "\000\025\300\250\312\205\000\000\000\000\000\000\000"}, 
  u_sockaddr_in = {sin_family = 2, sin_port = 5376, sin_addr = {
      s_addr = 2244651200}, sin_zero = "\000\000\000\000\000\000\000"}, 
  u_sockaddr_in6 = {sin6_family = 2, sin6_port = 5376, 
    sin6_flowinfo = 2244651200, sin6_addr = {__in6_u = {
        __u6_addr8 = '\000' <repeats 15 times>, __u6_addr16 = {0, 0, 0, 0, 0, 
          0, 0, 0}, __u6_addr32 = {0, 0, 0, 0}}}, sin6_scope_id = 0}}
(gdb) print p_sess->p_remote_addr->u
$3 = {u_sockaddr = {sa_family = 2, 
    sa_data = "\301\244\300\250\312\001\000\000\000\000\000\000\000"}, 
  u_sockaddr_in = {sin_family = 2, sin_port = 42177, sin_addr = {
      s_addr = 30058688}, sin_zero = "\000\000\000\000\000\000\000"}, 
  u_sockaddr_in6 = {sin6_family = 2, sin6_port = 42177, 
    sin6_flowinfo = 30058688, sin6_addr = {__in6_u = {
        __u6_addr8 = '\000' <repeats 15 times>, __u6_addr16 = {0, 0, 0, 0, 0, 
          0, 0, 0}, __u6_addr32 = {0, 0, 0, 0}}}, sin6_scope_id = 0}}
(gdb) print p_sess->parent_fd       
$4 = -1
(gdb) print p_sess->child_fd
$5 = 5

 

 

ftp 客户端的调试模式

ftp 客户端是有一个调试模式的, 虽然 还是很简单, 不过 可以获取到更多的信息 

vsftpd 登录过程的调试 中客户端和服务端的交互数据, 我们是通过 wireshark 来获取的所有的协议数据, 但是 这样未免太重了 

比如在 生产环境中, 可能我们并没有 wireshark 这样的工具, 可能就需要 ftp 客户端本身提供的一些调试选项 来尽可能的输出更多的信息 来辅助我们判断 

如下 便是例子, debug 模式开启之后, 客户端输出了 客户端发送给服务端的所有的命令 

呵呵 简单是简单, 一些情况下 可能会对你有很大的帮助的 

ftp> debug 
Debugging on (debug=1).
ftp> open 192.168.202.133
Connected to 192.168.202.133.
220-this is greeting
220 
Name (192.168.202.133:jerry): root
---> USER root
331 Please specify the password.
Password: 
---> PASS XXXX
230 Login successful.
ftp> ls
---> PORT 192,168,202,1,194,144
200 PORT command successful. Consider using PASV.
---> LIST
150 Here comes the directory listing.
drwxr-xr-x    3 0        0            4096 Dec 09 09:06 ClionWorkStations
drwxr-xr-x    2 0        0            4096 Dec 09 12:06 Desktop
drwxr-xr-x    2 0        0            4096 Dec 07 03:57 Documents
drwxr-xr-x    2 0        0            4096 Dec 09 11:59 Downloads
drwxr-xr-x    2 0        0            4096 Dec 07 03:57 Music
drwxr-xr-x    2 0        0            4096 Dec 07 03:57 Pictures
drwxr-xr-x    2 0        0            4096 Dec 07 03:57 Public
drwxr-xr-x    2 0        0            4096 Dec 07 03:57 Templates
drwxr-xr-x    2 0        0            4096 Dec 07 03:57 Videos
-rw-------    1 0        0            5744 Dec 14 11:01 vsftpd.log
226 Directory send OK.

 

 

======================= add at 2021.02.28 =======================
啊 有点累, 有点意外, 但是影响不大, 回家都 11点50 了 

 

 

完 

 

 

参考 

记vsftpd虚拟用户登录返回530 Login incorrect解决过程

vsftpd 登录过程的调试