aio-libs / aioftp

ftp client/server for asyncio (http://aioftp.readthedocs.org)
Apache License 2.0
185 stars 54 forks source link

get_passive_connection aborts immediately if epsv server response is 550 Permission denied #162

Closed kfred743 closed 1 year ago

kfred743 commented 1 year ago

Hi On some servers we get response 550 Permission denied for the EPSV command which causes the up/download to fail.

The usecase we want solve is to up/download a file using: ... aioftp.Client.context(...) as client: await client.upload(...)

The default behavior for up/download is to try passive connections with EPSV, then PASV which is good. However in the case where the FTP server have locked EPSV, then the aioftp internal function get_passive_connection fails and raises exception StatusCodeError 550 then aborts the whole operation. What we would like is that get_passive_connection tries EPSV then PASV, even if the first response is 550 (it works only if the response is 50x).

Example log where it fails: 230 Login successful. DEBUG TYPE I 200 Switching to Binary mode. EPSV 550 Permission denied.

(our printout) ==> ERROR upload failed: StatusCodeError Waiting for ('2xx',) but got 550 [' Permission denied.']

Are we doing anything wrong, or is there an existing way to control get_passive_connection to try both EPSV+PASV before giving up, even when the responses are 550?

pohmelie commented 1 year ago

As for rfc (https://www.rfc-editor.org/rfc/rfc2428.html) EPSV should never return 550 code. So this will not be fixed in aioftp in a such way as changing checking code routine. As a workaround, you can force client to use only PASV command by code like this:

async with aioftp.Client.context(..., passive_commands=("pasv",)) as client:
    ....

to exclude EPSV at all.

kfred743 commented 1 year ago

Thanks for looking into this. In the beginning I was thinking if any handling for epsv permission denied would be interesting to include in the automatic download handling inside aioftp since some servers by some reason responds like that.

But as you say, we can use passive_commands=("pasv",) as a workaround. Perhaps we will make our local application to try epsv first and catch the potential status code 550, then try again with only pasv if we have a ipv4 connection.

Thank you for your help (I will close this issue)