|
1 | 1 | 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" |
4 | 3 | import { TLSSocket, connect as connectTLS } from "tls" |
5 | 4 | import { FTPContext, FTPResponse, TaskResolver } from "./FtpContext" |
6 | 5 | 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" |
8 | 8 |
|
9 | 9 | export type UploadCommand = "STOR" | "APPE" |
10 | 10 |
|
@@ -83,7 +83,9 @@ export async function enterPassiveModeIPv4_forceControlHostIP(ftp: FTPContext): |
83 | 83 | // Strip IPv4-mapped IPv6 prefix (e.g. "::ffff:1.2.3.4" → "1.2.3.4") so the |
84 | 84 | // comparison works regardless of whether the OS uses a dual-stack socket. |
85 | 85 | 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) { |
87 | 89 | 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.`) |
88 | 90 | } |
89 | 91 | await connectForPassiveTransfer(normalizedControlHost, target.port, ftp) |
|
0 commit comments