Skip to content

Commit 7767a61

Browse files
committed
Resolve simple NAT issues
1 parent 7dda47e commit 7767a61

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

lib/ftp.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,13 @@ function enterPassiveModeIPv4(ftp) {
516516
task.reject("Can't parse PASV response: " + res.message);
517517
return;
518518
}
519+
// If the host in the PASV response has a local address while the control connection hasn't,
520+
// we assume a NAT issue and use the IP of the control connection as the target for the data connection.
521+
// We can't always perform this replacement because it's possible (although unlikely) that the FTP server
522+
// indeed uses a different host for data connections.
523+
if (ipIsPrivateAddress(target.host) && !ipIsPrivateAddress(ftp.socket.remoteAddress)) {
524+
target.host = ftp.socket.remoteAddress;
525+
}
519526
const handleConnErr = function(err) {
520527
task.reject("Can't open data connection in passive mode: " + err.message);
521528
};
@@ -572,6 +579,19 @@ function parseIPv4PasvResponse(message) {
572579
};
573580
}
574581

582+
/**
583+
* Returns true if an IP is a private address according to https://tools.ietf.org/html/rfc1918#section-3
584+
*
585+
* @param {string} ip The IP as a string, e.g. "192.168.0.1"
586+
* @returns {boolean} true if the ip is local.
587+
*/
588+
function ipIsPrivateAddress(ip = "") {
589+
const octets = ip.split(".").map(o => parseInt(o, 10));
590+
return octets[0] === 10 // 10.0.0.0 - 10.255.255.255
591+
|| (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) // 172.16.0.0 - 172.31.255.255
592+
|| (octets[0] === 192 && octets[1] === 168); // 192.168.0.0 - 192.168.255.255
593+
}
594+
575595
/**
576596
* Upload stream data as a file. For example:
577597
*

0 commit comments

Comments
 (0)