Skip to content

Commit eb95b0e

Browse files
committed
Take care of loopback addresses
1 parent 1901831 commit eb95b0e

2 files changed

Lines changed: 13 additions & 4 deletions

File tree

src/netUtils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ export function upgradeSocket(socket: Socket, options: ConnectionOptions): Promi
4646
})
4747
}
4848

49+
/**
50+
* Returns true if an IP address is a loopback address.
51+
*/
52+
export function isLoopback(ip: string): boolean {
53+
return ip === "::1" || ip.startsWith("127.")
54+
}
55+
4956
/**
5057
* Returns true if an IP is a private address according to https://tools.ietf.org/html/rfc1918#section-3.
5158
* This will handle IPv4-mapped IPv6 addresses correctly but return false for all other IPv6 addresses.

src/transfer.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { EventEmitter } from "events"
2-
import { describeAddress, describeTLS, ipIsPrivateV4Address } from "./netUtils"
3-
import { Writable, Readable, pipeline } from "stream"
2+
import { Readable, Writable, pipeline } from "stream"
43
import { TLSSocket, connect as connectTLS } from "tls"
54
import { FTPContext, FTPResponse, TaskResolver } from "./FtpContext"
65
import { ProgressTracker, ProgressType } from "./ProgressTracker"
7-
import { positiveIntermediate, positiveCompletion } from "./parseControlResponse"
6+
import { describeAddress, describeTLS, ipIsPrivateV4Address, isLoopback } from "./netUtils"
7+
import { positiveCompletion, positiveIntermediate } from "./parseControlResponse"
88

99
export type UploadCommand = "STOR" | "APPE"
1010

@@ -83,7 +83,9 @@ export async function enterPassiveModeIPv4_forceControlHostIP(ftp: FTPContext):
8383
// Strip IPv4-mapped IPv6 prefix (e.g. "::ffff:1.2.3.4" → "1.2.3.4") so the
8484
// comparison works regardless of whether the OS uses a dual-stack socket.
8585
const normalizedControlHost = controlHost.replace(/^::ffff:/i, "")
86-
if (normalizedControlHost !== target.host) {
86+
const hostsAreCompatible = normalizedControlHost === target.host
87+
|| (isLoopback(normalizedControlHost) && isLoopback(target.host))
88+
if (!hostsAreCompatible) {
8789
throw new Error(`PASV returned another host (${target.host}) for data transfer that you have connected to (${controlHost}). Even though the FTP protocol allows this, basic-ftp disables this feature by default for security reasons. If you do need this feature, instantiate the Client with the optional paramter "allowSeparateTransferHost: true". See the README documentation for more information.`)
8890
}
8991
await connectForPassiveTransfer(normalizedControlHost, target.port, ftp)

0 commit comments

Comments
 (0)