Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance CURLOPT_FTP_SKIP_PASV_IP #9758

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 15 additions & 8 deletions docs/libcurl/opts/CURLOPT_FTP_SKIP_PASV_IP.3
Expand Up @@ -29,14 +29,21 @@ CURLOPT_FTP_SKIP_PASV_IP \- ignore the IP address in the PASV response
.nf
#include <curl/curl.h>

CURLcode curl_easy_setopt(CURL *handle, CURLOPT_FTP_SKIP_PASV_IP, long skip);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_FTP_SKIP_PASV_IP, long rule);
.fi
.SH DESCRIPTION
Pass a long. If \fIskip\fP is set to 1, 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.
URL_FTP_SKIP_PASV_IP_ALWAYS instructs libcurl to ignore 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.
Since 7.87.0, the allowed values are:
.IP CURL_FTP_SKIP_PASV_IP_NEVER
Always use the IP returned by the server.
.IP CURL_FTP_SKIP_PASV_IP_ALWAYS
Always ignore the IP returned by the server.
.IP CURL_FTP_SKIP_PASV_IP_IF_NOT_ROUTABLE
Only ignore the returned IP if it is within the private address space (see RFC1918),
which is a common server configuration error.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should also mention from which version this option works.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated, but I guess @Zenju has better knowledge of what he wanted to do with this
PR


This option thus allows libcurl to work around broken server installations
that due to NATs, firewalls or incompetence report the wrong IP address
Expand All @@ -45,7 +52,7 @@ abuse by malicious servers.

This option has no effect if PORT, EPRT or EPSV is used instead of PASV.
.SH DEFAULT
1 since 7.74.0, was 0 before then.
CURL_FTP_SKIP_PASV_IP_ALWAYS since 7.87.0, was 1 from 7.74.0 to 7.87.0, was 0 before.
.SH PROTOCOLS
FTP
.SH EXAMPLE
Expand All @@ -55,7 +62,7 @@ if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/file.txt");

/* please ignore the IP in the PASV response */
curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, 1L);
curl_easy_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, CURL_FTP_SKIP_PASV_IP_ALWAYS);
ret = curl_easy_perform(curl);

curl_easy_cleanup(curl);
Expand Down
3 changes: 3 additions & 0 deletions docs/libcurl/symbols-in-versions
Expand Up @@ -38,6 +38,9 @@ CURL_FORMADD_NULL 7.9.8 7.56.0
CURL_FORMADD_OK 7.9.8 7.56.0
CURL_FORMADD_OPTION_TWICE 7.9.8 7.56.0
CURL_FORMADD_UNKNOWN_OPTION 7.9.8 7.56.0
CURL_FTP_SKIP_PASV_IP_ALWAYS 7.87.0
CURL_FTP_SKIP_PASV_IP_IF_NOT_ROUTABLE 7.87.0
CURL_FTP_SKIP_PASV_IP_NEVER 7.87.0
CURL_GLOBAL_ACK_EINTR 7.30.0
CURL_GLOBAL_ALL 7.8
CURL_GLOBAL_DEFAULT 7.8
Expand Down
6 changes: 6 additions & 0 deletions include/curl/curl.h
Expand Up @@ -2202,6 +2202,12 @@ typedef enum {
/* Convenient "aliases" */
#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER

/* parameters for the CURLOPT_FTP_SKIP_PASV_IP option */
#define CURL_FTP_SKIP_PASV_IP_NEVER 0L
#define CURL_FTP_SKIP_PASV_IP_ALWAYS 1L
#define CURL_FTP_SKIP_PASV_IP_IF_NOT_ROUTABLE 2L


/* These enums are for use with the CURLOPT_HTTP_VERSION option. */
enum {
CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd
Expand Down
26 changes: 25 additions & 1 deletion lib/ftp.c
Expand Up @@ -1829,6 +1829,18 @@ static char *control_address(struct connectdata *conn)
return conn->primary_ip;
}

static bool is_private_ip_v4(unsigned int ip[4])
{
if(ip[0] == 127 || /*127.0.0.0/8 (localhost)*/
ip[0] == 10 || /*10.0.0.0/8 (private)*/
(ip[0] == 192 && ip[1] == 168) || /*192.168.0.0/16 (private)*/
(ip[0] == 169 && ip[1] == 254) || /*169.254.0.0/16 (link-local)*/
(ip[0] == 172 && ip[1] / 16 == 1)) /*172.16.0.0/12 (private)*/
return false;
return true;
}


static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
int ftpcode)
{
Expand Down Expand Up @@ -1892,6 +1904,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* positive PASV response */
unsigned int ip[4] = {0, 0, 0, 0};
unsigned int port[2] = {0, 0};
bool skipIp;

/*
* Scan for a sequence of six comma-separated numbers and use them as
Expand All @@ -1917,7 +1930,18 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
}

/* we got OK from server */
if(data->set.ftp_skip_ip) {
skipIp = data->set.ftp_pasvp_ip_rule == CURL_FTP_SKIP_PASV_IP_ALWAYS;

if(data->set.ftp_pasvp_ip_rule == CURL_FTP_SKIP_PASV_IP_IF_NOT_ROUTABLE &&
!is_private_ip_v4(ip)) {
unsigned int ip_ctrl[4];
if(4 != sscanf(control_address(conn), "%u.%u.%u.%u",
&ip_ctrl[0], &ip_ctrl[1], &ip_ctrl[2], &ip_ctrl[3]) ||
is_private_ip_v4(ip_ctrl))
skipIp = true;
}

if(skipIp) {
/* told to ignore the remotely given IP but instead use the host we used
for the control connection */
infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead",
Expand Down
5 changes: 2 additions & 3 deletions lib/setopt.c
Expand Up @@ -1266,10 +1266,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)

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.
* Set up how to handle the IP that is returned by the server for PASV
*/
data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE;
data->set.ftp_pasvp_ip_rule = va_arg(param, long);
break;

case CURLOPT_FTP_ACCOUNT:
Expand Down
3 changes: 2 additions & 1 deletion lib/url.c
Expand Up @@ -532,7 +532,8 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */
set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */
set->ftp_filemethod = FTPFILE_MULTICWD;
set->ftp_skip_ip = TRUE; /* skip PASV IP by default */
set->ftp_pasvp_ip_rule = CURL_FTP_SKIP_PASV_IP_ALWAYS; /* skip PASV IP
by default */
#endif
set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */

Expand Down
4 changes: 2 additions & 2 deletions lib/urldata.h
Expand Up @@ -1869,8 +1869,8 @@ struct UserDefined {
BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */
BIT(ftp_use_eprt); /* if EPRT is to be attempted or not */
BIT(ftp_use_pret); /* if PRET is to be used before PASV or not */
BIT(ftp_skip_ip); /* skip the IP address the FTP server passes on to
us */
long ftp_pasvp_ip_rule; /* how to handle the IP address the FTP server
passes on to us */
BIT(wildcard_enabled); /* enable wildcard matching */
#endif
BIT(hide_progress); /* don't use the progress meter */
Expand Down
2 changes: 1 addition & 1 deletion src/tool_cfgable.c
Expand Up @@ -42,7 +42,7 @@ void config_init(struct OperationConfig *config)
config->tcp_nodelay = TRUE; /* enabled by default */
config->happy_eyeballs_timeout_ms = CURL_HET_DEFAULT;
config->http09_allowed = FALSE;
config->ftp_skip_ip = TRUE;
config->ftp_pasvp_ip_rule = CURL_FTP_SKIP_PASV_IP_ALWAYS;
config->file_clobber_mode = CLOBBER_DEFAULT;
}

Expand Down
3 changes: 2 additions & 1 deletion src/tool_cfgable.h
Expand Up @@ -177,7 +177,8 @@ struct OperationConfig {
bool doh_verifystatus;
bool create_dirs;
bool ftp_create_dirs;
bool ftp_skip_ip;
long ftp_pasvp_ip_rule; /* how to handle the IP address the FTP server
passes on to us */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should keep treating this as a boolean cmdline option

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

original author permits 3 values, so I don't think bool would fit here.

bool proxynegotiate;
bool proxyntlm;
bool proxydigest;
Expand Down
2 changes: 1 addition & 1 deletion src/tool_getparam.c
Expand Up @@ -1198,7 +1198,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
config->ignorecl = toggle;
break;
case 'q': /* --ftp-skip-pasv-ip */
config->ftp_skip_ip = toggle;
config->ftp_pasvp_ip_rule = CURL_FTP_SKIP_PASV_IP_ALWAYS;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this breaks behavior for users who do --no-ftp-skip-pasv-ip to switch off the option

break;
case 'r': /* --ftp-method (undocumented at this point) */
config->ftp_filemethod = ftpfilemethod(config, nextarg);
Expand Down
2 changes: 1 addition & 1 deletion src/tool_operate.c
Expand Up @@ -1931,7 +1931,7 @@ static CURLcode single_transfer(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, config->ignorecl?1L:0L);

/* curl 7.14.2 */
my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_skip_ip?1L:0L);
my_setopt(curl, CURLOPT_FTP_SKIP_PASV_IP, config->ftp_pasvp_ip_rule);

/* curl 7.15.1 */
if(proto_ftp)
Expand Down