diff --git a/docs/curl.1 b/docs/curl.1 index 40b29d049..0175b5a7c 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -347,6 +347,15 @@ If this option is used twice, the second will again disable silent failure. using this option can be used to override a previous --ftp-port option. (Added in 7.11.0) +If this option is used twice, the second will again disable silent failure. +.IP "--ftp-skip-pasv-ip" +(FTP) Tell curl to not use the IP address the server suggests in its response +to curl's PASV command when curl connects the data connection. Instead curl +will re-use the same IP address it already uses for the control +connection. (Added in 7.14.1) + +This option has no effect if PORT, EPRT or EPSV is used instead of PASV. + If this option is used twice, the second will again disable silent failure. .IP "--ftp-ssl" (FTP) Make the FTP connection switch to use SSL/TLS. (Added in 7.11.0) diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index ea009ca6c..aa351e959 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -767,6 +767,14 @@ curl is waiting for a response, this value overrides \fICURLOPT_TIMEOUT\fP. It is recommended that if used in conjunction with \fICURLOPT_TIMEOUT\fP, you set \fICURLOPT_FTP_RESPONSE_TIMEOUT\fP to a value smaller than \fICURLOPT_TIMEOUT\fP. (Added in 7.10.8) +.IP CURLOPT_FTP_SKIP_PASV_IP +Pass a long. If set to a non-zero value, it instructs libcurl to not use the +IP address the server suggests in its 227-response to libcurl's PASV command +when libcurl connects the data connection. Instead libcurl will re-use the +same IP address it already uses for the control connection. But it will use +the port number from the 227-response. (Added in 7.14.1) + +This option has no effect if PORT, EPRT or EPSV is used instead of PASV. .IP CURLOPT_FTP_SSL Pass a long using one of the values from below, to make libcurl use your desired level of SSL for the ftp transfer. (Added in 7.11.0) diff --git a/include/curl/curl.h b/include/curl/curl.h index daacf781a..48c896119 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -903,6 +903,12 @@ typedef enum { /* ignore Content-Length */ CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/ftp.c b/lib/ftp.c index bc30e2258..4d89723c7 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1601,8 +1601,18 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, return CURLE_FTP_WEIRD_227_FORMAT; } - snprintf(newhost, sizeof(newhost), - "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); + /* we got OK from server */ + if(data->set.ftp_skip_ip) { + /* told to ignore the remotely given IP but instead use the one we used + for the control connection */ + infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n", + ip[0], ip[1], ip[2], ip[3], + conn->ip_addr_str); + snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str); + } + else + snprintf(newhost, sizeof(newhost), + "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); newport = (port[0]<<8) + port[1]; } else if(ftp->count1 == 0) { @@ -1622,8 +1632,6 @@ static CURLcode ftp_state_pasv_resp(struct connectdata *conn, return CURLE_FTP_WEIRD_PASV_REPLY; } - /* we got OK from server */ - if(data->change.proxy && *data->change.proxy) { /* * This is a tunnel through a http proxy and we need to connect to the diff --git a/lib/url.c b/lib/url.c index 06784fe34..c256cf23f 100644 --- a/lib/url.c +++ b/lib/url.c @@ -965,6 +965,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->set.ftp_use_epsv = va_arg(param, long)?TRUE:FALSE; break; + case CURLOPT_FTP_SKIP_PASV_IP: + /* + * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the + * bypass of the IP address in PASV responses. + */ + data->set.ftp_skip_ip = (bool)va_arg(param, long); + break; + case CURLOPT_INFILE: /* * FILE pointer to read the file to be uploaded from. Or possibly @@ -1240,7 +1248,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, /* * Set a SSL_CTX callback */ - data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); + data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); break; case CURLOPT_SSL_CTX_DATA: /* diff --git a/lib/urldata.h b/lib/urldata.h index 3436b17da..ff0b23a54 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1072,8 +1072,9 @@ struct UserDefined { bool no_signal; /* do not use any signal/alarm handler */ bool global_dns_cache; /* subject for future removal */ bool tcp_nodelay; /* whether to enable TCP_NODELAY or not */ - bool ignorecl; /* ignore content length */ + bool ftp_skip_ip; /* skip the IP address the FTP server passes on to + us */ }; /* diff --git a/src/main.c b/src/main.c index e0eb6bbc7..dda4dab80 100644 --- a/src/main.c +++ b/src/main.c @@ -315,6 +315,7 @@ struct Configurable { bool insecure_ok; /* set TRUE to allow insecure SSL connects */ bool create_dirs; bool ftp_create_dirs; + bool ftp_skip_ip; bool proxyntlm; bool proxydigest; bool proxybasic; @@ -508,7 +509,8 @@ static void help(void) " --crlf Convert LF to CRLF in upload", " -f/--fail Fail silently (no output at all) on errors (H)", " --ftp-create-dirs Create the remote dirs if not present (F)", - " --ftp-pasv Use PASV instead of PORT (F)", + " --ftp-pasv Use PASV/EPSV instead of PORT (F)", + " --ftp-skip-pasv-ip Skip the IP address for PASV (F)\n" " --ftp-ssl Enable SSL/TLS for the ftp transfer (F)", " -F/--form Specify HTTP multipart POST data (H)", " --form-string Specify HTTP multipart POST data (H)", @@ -1313,6 +1315,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ {"$n", "proxy-anyauth", FALSE}, {"$o", "trace-time", FALSE}, {"$p", "ignore-content-length", FALSE}, + {"$q", "ftp-skip-pasv-ip", FALSE}, {"0", "http1.0", FALSE}, {"1", "tlsv1", FALSE}, @@ -1709,6 +1712,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */ case 'p': /* --ignore-content-length */ config->ignorecl ^= TRUE; break; + case 'q': /* --ftp-skip-pasv-ip */ + config->ftp_skip_ip ^= TRUE; + break; } break; case '#': /* --progress-bar */ @@ -3905,6 +3911,10 @@ operate(struct Configurable *config, int argc, char *argv[]) curl_easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl); + /* curl 7.14.1 */ + curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, + config->ftp_skip_ip); + retry_numretries = config->req_retry; retrystart = curlx_tvnow(); diff --git a/tests/FILEFORMAT b/tests/FILEFORMAT index 57850fb64..e862f90c8 100644 --- a/tests/FILEFORMAT +++ b/tests/FILEFORMAT @@ -81,6 +81,7 @@ RETRWEIRDO RETRNOSIZE NOSAVE SLOWDOWN +PASVBADIP - makes PASV send back an illegal IP in its 227 response For HTTP, one specified command is supported: "auth_required" - if this is set and a POST/PUT is made without auth, the diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 416c2bb60..0b9e4a2ea 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -33,4 +33,4 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \ test237 test238 test239 test243 test245 test246 test247 test248 test249 \ test250 test251 test252 test253 test254 test255 test521 test522 test523 \ test256 test257 test258 test259 test260 test261 test262 test263 test264 \ - test265 test266 test267 test268 test269 + test265 test266 test267 test268 test269 test270 diff --git a/tests/data/test270 b/tests/data/test270 new file mode 100644 index 000000000..6c47b559e --- /dev/null +++ b/tests/data/test270 @@ -0,0 +1,48 @@ + + +FTP +PASV +RETR + + +# Server-side + + +data + to + see +that FTP +works + so does it? + + +PASVBADIP + + + +# Client-side + + +ftp + + +FTP RETR PASV --ftp-skip-pasv-ip + + +ftp://%HOSTIP:%FTPPORT/270 --ftp-skip-pasv-ip --disable-epsv + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS curl_by_daniel@haxx.se +PWD +PASV +TYPE I +SIZE 270 +RETR 270 +QUIT + + diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index e721bec2a..4c74cc1cf 100644 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -70,6 +70,7 @@ sub ftpmsg { } my $verbose=0; # set to 1 for debugging +my $pasvbadip=0; my $retrweirdo=0; my $retrnosize=0; my $srcdir="."; @@ -564,7 +565,11 @@ sub PASV_command { if($cmd ne "EPSV") { # PASV reply - sendcontrol sprintf("227 Entering Passive Mode (127,0,0,1,%d,%d)\n", + my $p="127,0,0,1"; + if($pasvbadip) { + $p="1,2,3,4"; + } + sendcontrol sprintf("227 Entering Passive Mode ($p,%d,%d)\n", ($pasvport/256), ($pasvport%256)); } else { @@ -703,6 +708,10 @@ sub customize { logmsg "FTPD: instructed to use RETRNOSIZE\n"; $retrnosize=1; } + elsif($_ =~ /PASVBADIP/) { + logmsg "FTPD: instructed to use PASVBADIP\n"; + $pasvbadip=1; + } elsif($_ =~ /NOSAVE/) { # don't actually store the file we upload - to be used when # uploading insanely huge amounts