vsftpd returns 0,0,0,0 响应 PASV
vsftpd returns 0,0,0,0 in response to PASV
我在 AWS EC2 (Ubuntu16.04) 上用被动模式 (PASV
) 设置了一个 FTP 服务器,但它不起作用。但是,它适用于 EPSV
,不知道为什么。我四处搜索但没有找到答案,任何人都可以帮助我吗?
1. vsftpd 配置
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
pasv_enable=YES
pasv_min_port=13000
pasv_max_port=13100
port_enable=YES
pasv_address=[public ip address of AWS EC2 instance]
allow_writeable_chroot=YES
seccomp_sandbox=NO
2。 AWS EC2 防火墙
3。历经考验FTP
使用PASV
模式,我无法连接到FTP服务器,日志是:
220 (vsFTPd 3.0.3)
USER sensor
331 Please specify the password.
PASS (password not shown)
230 Login successful.
CWD /
250 Directory successfully changed.
TYPE A
200 Switching to ASCII mode.
PASV
QUIT
然而,它与 EPSV
一起使用(选中 IPV6 复选框),日志如下:
220 (vsFTPd 3.0.3)
USER sensor
331 Please specify the password.
PASS (password not shown)
230 Login successful.
PWD
257 "/" is the current directory
TYPE A
200 Switching to ASCII mode.
EPSV
229 Entering Extended Passive Mode (|||13082|)
LIST
150 Here comes the directory listing.
226 Directory send OK.
4.通过Python ftplib
测试
from ftplib import FTP
contents = []
ftp = FTP(host=xxx, timeout=3000)
ftp.login(user=xxx, passwd=xxx)
ftp.set_debuglevel(2)
ftp.retrlines("NLST", contents.append)
ftp.quit()
日志如下:
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Switching to ASCII mode.\n'
*resp* '200 Switching to ASCII mode.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (0,0,0,0,50,245).\n'
*resp* '227 Entering Passive Mode (0,0,0,0,50,245).'
ConnectionRefusedError: [Errno 111] Connection refused
我觉得这像是 vsftpd 中的一个错误。
从代码看来,如果设置了 public pasv_address
,它将始终发送 0,0,0,0
,但服务器有一个(本地)IPv6 地址。
要解决此问题,请确保服务器不侦听 IPv6 地址(默认行为是什么,您可以通过设置 listen_ipv6=YES
覆盖它):
listen_ipv6=NO
listen=YES
唯一的其他解决方案是删除私有 IPv6 地址,如果在 EC2 中可能的话。
或者使用另一个 FTP 服务器,例如ProFTPD.
或者让 ftplib 忽略服务器返回的 IP 地址。
参见
为了证明这确实是一个错误,请查看最新 vsftpd 版本 (3.0.3) 的代码:
handle_pasv
在 postlogin.c
:
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
...
if (tunable_pasv_address != 0)
{
vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr);
/* Report passive address as specified in configuration */
if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0)
{
die("invalid pasv_address");
}
}
else
{
vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
}
str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
if (!is_ipv6)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
}
else
{
const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
if (p_v4addr)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
}
else
{
str_append_text(&s_pasv_res_str, "0,0,0,0");
}
}
其中 vsf_sysutil_sockaddr_ipv6_v4
returns 0,如果 s_p_sockaddr
不是 IPv6,当设置 pasv_address
时它永远不会是 IPv6。
sysutil.c
:
const void*
vsf_sysutil_sockaddr_ipv6_v4(const struct vsf_sysutil_sockaddr* p_addr)
{
static unsigned char pattern[12] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
const unsigned char* p_addr_start;
if (p_addr->u.u_sockaddr.sa_family != AF_INET6)
{
return 0;
}
if (vsf_sysutil_memcmp(pattern, &p_addr->u.u_sockaddr_in6.sin6_addr, 12))
{
return 0;
}
p_addr_start = (const unsigned char*)&p_addr->u.u_sockaddr_in6.sin6_addr;
return &p_addr_start[12];
}
恕我直言,代码有误。当从 p_sess->p_local_addr
“自动检测”到 IP 地址时,它可以工作(并且有意义),但是当使用 pasv_address
地址时失败。
考虑将此报告给 vsftpd 的作者。
保留 PASV
与 EPSV
的原始解释:
只是为了解释PASV
和EPSV
之间的区别: PASV
returns响应中的一个IP地址。该信息有 99.9% 是冗余的。当服务器不知道其外部 IP 地址时,它通常会导致问题。
EPSV
比 PASV
晚引入,当时很明显响应中的 IP 地址存在问题。所以对于 EPSV
,只包含一个端口号。客户端隐式连接到 FTP 服务器 IP 地址。
如果服务器真的returns 0,0,0,0
响应了PASV
命令,很明显为什么客户端无法连接到服务器,当PASV
是用过。
我在 AWS EC2 (Ubuntu16.04) 上用被动模式 (PASV
) 设置了一个 FTP 服务器,但它不起作用。但是,它适用于 EPSV
,不知道为什么。我四处搜索但没有找到答案,任何人都可以帮助我吗?
1. vsftpd 配置
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
pasv_enable=YES
pasv_min_port=13000
pasv_max_port=13100
port_enable=YES
pasv_address=[public ip address of AWS EC2 instance]
allow_writeable_chroot=YES
seccomp_sandbox=NO
2。 AWS EC2 防火墙
3。历经考验FTP
使用PASV
模式,我无法连接到FTP服务器,日志是:
220 (vsFTPd 3.0.3)
USER sensor
331 Please specify the password.
PASS (password not shown)
230 Login successful.
CWD /
250 Directory successfully changed.
TYPE A
200 Switching to ASCII mode.
PASV
QUIT
然而,它与 EPSV
一起使用(选中 IPV6 复选框),日志如下:
220 (vsFTPd 3.0.3)
USER sensor
331 Please specify the password.
PASS (password not shown)
230 Login successful.
PWD
257 "/" is the current directory
TYPE A
200 Switching to ASCII mode.
EPSV
229 Entering Extended Passive Mode (|||13082|)
LIST
150 Here comes the directory listing.
226 Directory send OK.
4.通过Python ftplib
测试from ftplib import FTP
contents = []
ftp = FTP(host=xxx, timeout=3000)
ftp.login(user=xxx, passwd=xxx)
ftp.set_debuglevel(2)
ftp.retrlines("NLST", contents.append)
ftp.quit()
日志如下:
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Switching to ASCII mode.\n'
*resp* '200 Switching to ASCII mode.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (0,0,0,0,50,245).\n'
*resp* '227 Entering Passive Mode (0,0,0,0,50,245).'
ConnectionRefusedError: [Errno 111] Connection refused
我觉得这像是 vsftpd 中的一个错误。
从代码看来,如果设置了 public pasv_address
,它将始终发送 0,0,0,0
,但服务器有一个(本地)IPv6 地址。
要解决此问题,请确保服务器不侦听 IPv6 地址(默认行为是什么,您可以通过设置 listen_ipv6=YES
覆盖它):
listen_ipv6=NO
listen=YES
唯一的其他解决方案是删除私有 IPv6 地址,如果在 EC2 中可能的话。
或者使用另一个 FTP 服务器,例如ProFTPD.
或者让 ftplib 忽略服务器返回的 IP 地址。
参见
为了证明这确实是一个错误,请查看最新 vsftpd 版本 (3.0.3) 的代码:
handle_pasv
在 postlogin.c
:
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
...
if (tunable_pasv_address != 0)
{
vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr);
/* Report passive address as specified in configuration */
if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0)
{
die("invalid pasv_address");
}
}
else
{
vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
}
str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
if (!is_ipv6)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
}
else
{
const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
if (p_v4addr)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
}
else
{
str_append_text(&s_pasv_res_str, "0,0,0,0");
}
}
其中 vsf_sysutil_sockaddr_ipv6_v4
returns 0,如果 s_p_sockaddr
不是 IPv6,当设置 pasv_address
时它永远不会是 IPv6。
sysutil.c
:
const void*
vsf_sysutil_sockaddr_ipv6_v4(const struct vsf_sysutil_sockaddr* p_addr)
{
static unsigned char pattern[12] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
const unsigned char* p_addr_start;
if (p_addr->u.u_sockaddr.sa_family != AF_INET6)
{
return 0;
}
if (vsf_sysutil_memcmp(pattern, &p_addr->u.u_sockaddr_in6.sin6_addr, 12))
{
return 0;
}
p_addr_start = (const unsigned char*)&p_addr->u.u_sockaddr_in6.sin6_addr;
return &p_addr_start[12];
}
恕我直言,代码有误。当从 p_sess->p_local_addr
“自动检测”到 IP 地址时,它可以工作(并且有意义),但是当使用 pasv_address
地址时失败。
考虑将此报告给 vsftpd 的作者。
保留 PASV
与 EPSV
的原始解释:
只是为了解释PASV
和EPSV
之间的区别: PASV
returns响应中的一个IP地址。该信息有 99.9% 是冗余的。当服务器不知道其外部 IP 地址时,它通常会导致问题。
EPSV
比 PASV
晚引入,当时很明显响应中的 IP 地址存在问题。所以对于 EPSV
,只包含一个端口号。客户端隐式连接到 FTP 服务器 IP 地址。
如果服务器真的returns 0,0,0,0
响应了PASV
命令,很明显为什么客户端无法连接到服务器,当PASV
是用过。