Client.prepareTransfer() only tries first (IPV6) strategy then fails on system ETIMEDOUT error
See original GitHub issueDescription
I am trying to list files from a server with an IPV4 address. With the ‘list()’ operation, the code eventually enters Client.prepareTransfer()
which in turn tries to connect with two strategies: IPV6 then IPV4. However I’m getting an ETIMEDOUT error when attempting the IPV6 connection (not suprising), but then the code re-throws this error rather than trying the next strategy (IPV4). You see this here:
ftp.log("Trying to find optimal transfer strategy...");
for (const strategy of strategies) {
try {
const res = await strategy(ftp);
ftp.log("Optimal transfer strategy found.");
this.prepareTransfer = strategy; // eslint-disable-line require-atomic-updates
return res;
}
catch (err) {
// Receiving an FTPError means that the last transfer strategy failed and we should
// try the next one. Any other exception should stop the evaluation of strategies because
// something else went wrong.
if (!(err instanceof FtpContext_1.FTPError)) {
throw err;
}
}
}
Could this catch
be an empty block or explicitly test for ETIMEOUT?
Example code
// -- main ----------------------------
const main = async function()
{
const client = new ftp.Client(CONFIG.get('ftp.timeout'));
client.ftp.verbose = true;
await client.access(
{
host: CONFIG.get('ftp.host'),
user: CONFIG.get('ftp.user'),
password: CONFIG.get('ftp.password'),
secure: false
});
console.log(await client.cd('/VDOT Sites'));
console.log(await client.list());
if (!client.closed)
{
await client.close();
}
};
main().catch((err) =>
{
let msg = err.message;
if (err.details !== undefined) msg += ' : ' + err.details;
console.log(msg);
process.exit(1);
});
Console output
Login security: No encryption
> USER ****
< 331 Password required for ****
> PASS ###
< 230 Logged on
> TYPE I
< 200 Type set to I
> STRU F
< 200 Using file structure 'File'
> OPTS UTF8 ON
< 202 UTF8 mode is always enabled. No need to send this command.
> OPTS MLST type;size;modify;unique;unix.mode;unix.owner;unix.group;unix.ownername;unix.groupname;
< 200 MLST OPTS type;size;modify;
> CWD /VDOT Sites
< 250 CWD successful. "/VDOT Sites" is current directory.
{
code: 250,
message: '250 CWD successful. "/VDOT Sites" is current directory.'
}
Trying to find optimal transfer strategy...
> EPSV
< 229 Entering Extended Passive Mode (|||10034|)
Can't open data connection in passive mode: connect ETIMEDOUT ***.170.37.65:10034
Which version of Node.js are you using? Node v12.16.1 with basic-ftp v4.5.3
Additional context
If I only attempt the IPV4 strategy (enterPassiveModeIPv4
) by modifying the Client
constructor, everything then works.
Aside: Also can you swap the order of the strategies (i.e. IPV4 before IPV6) as that would be faster as I suspect many more connections are attempted on IPV4?
Issue Analytics
- State:
- Created 4 years ago
- Comments:5 (2 by maintainers)
Top GitHub Comments
Ah, I see the reason now. Very interesting!
Your data connection points to a different IP than the control connection. FTP uses two different socket connections, one for general commands and another one for transferring data (upload, download, directory listing). Usually these point to the same IP but the original FTP protocol also considered the use-case we see here: That the data connection is pointing to another machine. Is this the case here? Can you share more of your context?
This has been used very rarely though. Which is why the EPSV command, the more modern variant of PASV, which is used to establish such a data connection, doesn’t allow it anymore. That’s why only PASV works in your case.
You don’t have to overwrite the library for this. Use the following lines to force your client to use, well, PASV. Turns out the name of
enterPassiveModeIPv4
is not so great considering what I’ve just explained. It should be enterPassiveModePASV.hello how do i change the
const ftp = require(“basic-ftp”)
client.prepareTransfer = ftp.enterPassiveModeIPv4
on: push: branches: - master
name: 🚀 Deploy website on push jobs: web-deploy: name: 🎉 Deploy runs-on: ubuntu-latest steps: - name: 🚚 Get latest code uses: actions/checkout@v2