My application connects to FTP clients that are connected to the server via VPN. So far all clients supported active PHP and the library I used went with active FTP by default, so there was no issue. Now however we have installed some new clients that don’t properly work over active PHP, so I found out that from the server (via the FTP cli and FileZilla over RDP) can connect to and talk to all clients via a passive connection.
When trying to establish a passive connection in PHP however, all FTP commands (such as nlist
) just time out. I came across this blog post, which provides a patch for a specific passive FTP issue (which is now available in regular PHP versions), so I tried testing it like so:
$conn = ftp_connect($address); $login = ftp_login($conn, 'username', 'password'); ftp_set_option($conn, USEPASVADDRESS, false); ftp_pasv($conn, true); $contents = ftp_nlist($conn, '.'); var_dump($contents); ftp_close($conn);
ftp_pasv
returns true, so it is switching over to passive mode, but ftp_nlist
times out, which it doesn’t for active mode and the compatible clients.
What is so different between PHPs FTP implementation and the FTP cli application?
Advertisement
Answer
I think I have figured out what the exact issue was in my case. I’m not sure if this helps anyone else, but it might be an idea to look into if you’re having similar issues.
When activating passive mode, the server tells you an IP and a port to connect to for data transfer. When using the ftp
CLI the actual PASV
command is only sent when you send a data command (like requesting folder content). In those cases the CLI sends the PASV
command, receives an IP/port, connects to it and then sends the original command (LIST
) through that.
PHP seems to do this differently: As soon as you switch to a passive connection in our code, it sends the PASV
command and immediately receives the IP/port from the server – But PHP doesn’t yet connect to it. When I then ask for a directory listing, first the ASCII mode gets activated and only then does it connect to the previously received IP/Port to send the LIST
command.
My suspicion therefore was that the FTP server I’m connecting to is faulty. My idea was that once it receives a PASV
command it waits until someone actually connects to that passive connection, before even starting to accept any other command. In fact I managed to verify just that. While the PHP script was still waiting for a response, I manually established a TCP connection to the received passive port, after which my PHP code resumed running and showing me the results.
Unfortunately for me that means I can’t use passive FTP at all with those servers and updating/switching to a different server implementation is not possible. Active FTP also is pretty much impossible since my app is running in Docker.