如何调试PHP FTP在PASV模式下工作的原因,当控制台FTP似乎工作正常时?

时间:2017-10-30 16:33:22

标签: php ftp passive-mode

我有一个用于测试的Docker Compose系统,其中我正在对单页Web应用程序进行端到端测试。网站中的几个按钮将导致在一个容器(missive-transmitter)中启动FTP连接,转到另一个容器(missive-testbox)中的测试FTP服务器。

我在PHP中的FTP逻辑总是使用"被动"模式,我认为这是导致问题。我创建了一个在missive-transmitter中运行的脚本,这是真实内容的简化版本。它如下所示,直接从控制台运行:

<?php
# ftptest.php

error_reporting(-1);
ini_set('display_errors', true);

$conn = ftp_connect('missive-testbox', 21);

$ok1 = ftp_login($conn, 'missive_test', 'password');
if (!$ok1)
{
    die("Cannot log in\n");
}

// *** Start problem section
$ok2 = ftp_pasv($conn, true);
if (!$ok2)
{
    die("Cannot switch to passive mode\n");
}
// *** End problem section

$info = ftp_systype($conn);
echo "Info: $info\n";

$ok3 = ftp_put($conn, 'ftptest.php', 'ftptest.php', FTP_ASCII);
if (!$ok3)
{
    die("Cannot send a file\n");
}

现在,如果我删除***部分(启用被动模式),那么脚本将起作用。如果我把它留下来,我明白了:

  

信息:UNIX

     

警告:ftp_put():php_connect_nonb()失败:第23行/root/src/ftptest.php中正在进行的操作(115)

     

警告:ftp_put():TYPE现在是第23行/root/src/ftptest.php中的ASCII

     

无法发送文件

我希望我的FTP操作能够在PASV模式下工作。

奇怪的是,如果我安装了一个FTP客户端,那么它似乎可以在主动或被动模式下工作,这是我不明白的。在missive-transmitter方面:

~/src $ # This is the `sh` shell in `missive-transmitter`
~/src $ #
~/src $ # Install LFTP in Alpine environment
~/src $ apk add lftp
~/src $ lftp missive_test@missive-testbox
Password: 
lftp missive_test@missive-testbox:~> set ftp:passive-mode off         
lftp missive_test@missive-testbox:~> put ftptest.php       
457 bytes transferred                            
lftp missive_test@missive-testbox:/> set ftp:passive-mode on 
lftp missive_test@missive-testbox:/> put ftptest.php        
457 bytes transferred
lftp missive_test@missive-testbox:/> 

PHP是否采用了不同的方式,或者我是否在控制台客户端中实际使用PASV模式?

我已确认两个容器可以从各自的ping控制台相互sh。它们位于同一个(自定义)Docker网络上。

missive-testbox Docker容器基于gists/pure-ftpd,因此据我所知,它应该正确配置。

更新

下面的答案中有用的一点是关于NAT如何使一方使用错误的IP地址建立连接。但是,IP地址似乎位于同一子网上,但我不是网络专家。

来自missive-transmitter

~ # ping missive-testbox
PING missive-testbox (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.076 ms

来自missive-testbox

~ # ping missive-transmitter
PING missive-transmitter (172.19.0.4): 56 data bytes
64 bytes from 172.19.0.4: seq=0 ttl=64 time=0.119 ms

认为他们都是172.19.0.x地址的事实意味着他们应该能够完全看到对方,尽管我愿意对这个假设进行更正。

更新2

有人建议获取一些FTP客户端或服务器日志是调试它的好方法。客户很容易。以下是与上述相同的操作,但是在LFTP的调试模式中。

活动模式是第一个:

~/src # lftp -d missive_test@missive-testbox
Password: 
---- Resolving host address...
---- 1 address found: 172.19.0.2
lftp missive_test@missive-testbox:~> set ftp:passive-mode off
lftp missive_test@missive-testbox:~> put ftptest.php
---- Connecting to missive-testbox (172.19.0.2) port 21
<--- 220-Welcome to Pure-FTPd.
<--- 220-You are user number 1 of 5 allowed.
<--- 220-Local time is now 17:54. Server port: 21.
<--- 220-This is a private system - No anonymous login
<--- 220-IPv6 connections are also welcome on this server.
<--- 220 You will be disconnected after 15 minutes of inactivity.
---> FEAT
<--- 530 You aren't logged in
---> AUTH TLS
<--- 500 This security scheme is not implemented
---> USER missive_test
<--- 331 User missive_test OK. Password required
---> PASS XXXX
<--- 230 OK. Current directory is /              
---> FEAT
<--- 500 Unknown command
---> PWD
<--- 257 "/" is your current location
---> TYPE I
<--- 200 TYPE is now 8-bit binary
---> PORT 172,19,0,4,159,62
<--- 200 PORT command successful
---> ALLO 457
<--- 500 Unknown command
---> STOR ftptest.php
---- Accepted data connection from (172.19.0.2) port 20
<--- 150 Connecting to port 40766
---- Closing data socket
<--- 226-File successfully transferred
<--- 226 0.000 seconds (measured here), 3.16 Mbytes per second
---> SITE UTIME 20171030154823 ftptest.php
<--- 500 Unknown command
---> SITE UTIME ftptest.php 20171030154823 20171030154823 20171030154823 UTC
<--- 500 Unknown command
457 bytes transferred

好的,那是成功的。这是LFTP中的被动版本,再次成功。

我注意到一开始的警告,关于需要修复的地址 - 这可能是相关的吗?如果任何一方将自己宣传给另一方作为&#34; localhost&#34;,那可能是一个问题:-)

lftp missive_test@missive-testbox:/> set ftp:passive-mode on 
lftp missive_test@missive-testbox:/> put ftptest.php        
---> PASV
<--- 227 Entering Passive Mode (127,0,0,1,117,54)
---- Address returned by PASV seemed to be incorrect and has been fixed
---- Connecting data socket to (172.19.0.2) port 30006
---- Data connection established
---> STOR ftptest.php
<--- 150 Accepted data connection
---- Closing data socket
<--- 226-File successfully transferred
<--- 226 0.000 seconds (measured here), 1.79 Mbytes per second
457 bytes transferred

2 个答案:

答案 0 :(得分:1)

很难说这里完成了哪些FTP操作。但可能是PHP正在使用PASV而lftp使用EPSV来设置被动模式。

如果PASV,服务器会发送IP地址和端口号,等待连接。使用EPSV服务器只提供端口号,目标IP地址是当前FTP控制连接中的IP地址。如果涉及NAT(网络地址转换)(在Docker设置中不太可能),与从FTP客户端外部可见的服务器相比,服务器可能会看到不同的内部IP地址,这意味着客户端无法连接到PASV命令的响应中给出的(错误的)IP地址。使用EPSV此问题不存在,因为客户端不使用服务器提供的IP地址作为目标。

答案 1 :(得分:0)

有用的答案和评论的组合,加上LFTP的警告,使我找到了解决方案。这个问题与PASV模式下的IP可见性有关,正如Steffen所推测的那样。 LFTP客户端已经帮助检测到我的服务器配置错误,因此透明地修复了它,这就是混乱的发生地。

值得注意的是,PHP不是那么聪明或善良 - 它没有这样的修复(当然技术上是正确的)。

配置的原因是default Dockerfile settings

ENV PUBLIC_HOST localhost

所以,我现在所做的,是用我的自己的Dockerfile替换它与LAN IP:

ENV PUBLIC_HOST 172.19.0.2

稍后重新启动,修复消息从LFTP消失,我的PHP脚本正常工作。

更新

由于我不知道我是否可以依赖上述IP地址的静态性,我已将其重置为容器的名称:

ENV PUBLIC_HOST missive-testbox

这似乎工作正常。