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

Cannot set options required for FTP transfers in libcurl with HTTP and MQTT disabled #15634

Closed
momontyo opened this issue Nov 25, 2024 · 3 comments

Comments

@momontyo
Copy link

momontyo commented Nov 25, 2024

I did this

I have built libcurl using curl 8.11.0 sources, with only FTP/FTPS enabled.
This version responds with CURLE_UNKNOWN_OPTION(48) with the following option settings.

CURLcode result = curl_easy_setopt(curl, CURLOPT_USERNAME, username);

When tracked down in the debug run, CURLOPT_USERNAME and a number of other optional operating conditions in setopt_cptr were disabled in the compile condition.

The #if block starting with the following condition has a lot of impact and the #endif comment is incorrect.
Is there some error in manipulating nested relationships, etc.?

#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT)

curl/lib/setopt.c

Lines 1723 to 2197 in ae016b0

#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT)
case CURLOPT_COPYPOSTFIELDS:
/*
* A string with POST data. Makes curl HTTP POST. Even if it is NULL.
* If needed, CURLOPT_POSTFIELDSIZE must have been set prior to
* CURLOPT_COPYPOSTFIELDS and not altered later.
*/
if(!ptr || data->set.postfieldsize == -1)
result = Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], ptr);
else {
if(data->set.postfieldsize < 0)
return CURLE_BAD_FUNCTION_ARGUMENT;
#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T
/*
* Check that requested length does not overflow the size_t type.
*/
else if(data->set.postfieldsize > SIZE_T_MAX)
return CURLE_OUT_OF_MEMORY;
#endif
else {
/* Allocate even when size == 0. This satisfies the need of possible
later address compare to detect the COPYPOSTFIELDS mode, and to
mark that postfields is used rather than read function or form
data.
*/
char *p = Curl_memdup0(ptr, (size_t)data->set.postfieldsize);
if(!p)
return CURLE_OUT_OF_MEMORY;
else {
free(data->set.str[STRING_COPYPOSTFIELDS]);
data->set.str[STRING_COPYPOSTFIELDS] = p;
}
}
}
data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS];
data->set.method = HTTPREQ_POST;
break;
case CURLOPT_POSTFIELDS:
/*
* Like above, but use static data instead of copying it.
*/
data->set.postfields = ptr;
/* Release old copied data. */
Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]);
data->set.method = HTTPREQ_POST;
break;
#ifndef CURL_DISABLE_HTTP
case CURLOPT_ACCEPT_ENCODING:
/*
* String to use at the value of Accept-Encoding header.
*
* If the encoding is set to "" we use an Accept-Encoding header that
* encompasses all the encodings we support.
* If the encoding is set to NULL we do not send an Accept-Encoding header
* and ignore an received Content-Encoding header.
*
*/
if(ptr && !*ptr) {
char all[256];
Curl_all_content_encodings(all, sizeof(all));
return Curl_setstropt(&data->set.str[STRING_ENCODING], all);
}
return Curl_setstropt(&data->set.str[STRING_ENCODING], ptr);
#if !defined(CURL_DISABLE_AWS)
case CURLOPT_AWS_SIGV4:
/*
* String that is merged to some authentication
* parameters are used by the algorithm.
*/
result = Curl_setstropt(&data->set.str[STRING_AWS_SIGV4], ptr);
/*
* Basic been set by default it need to be unset here
*/
if(data->set.str[STRING_AWS_SIGV4])
data->set.httpauth = CURLAUTH_AWS_SIGV4;
break;
#endif
case CURLOPT_REFERER:
/*
* String to set in the HTTP Referer: field.
*/
if(data->state.referer_alloc) {
Curl_safefree(data->state.referer);
data->state.referer_alloc = FALSE;
}
result = Curl_setstropt(&data->set.str[STRING_SET_REFERER], ptr);
data->state.referer = data->set.str[STRING_SET_REFERER];
break;
case CURLOPT_USERAGENT:
/*
* String to use in the HTTP User-Agent field
*/
return Curl_setstropt(&data->set.str[STRING_USERAGENT], ptr);
#if !defined(CURL_DISABLE_COOKIES)
case CURLOPT_COOKIE:
/*
* Cookie string to send to the remote server in the request.
*/
return Curl_setstropt(&data->set.str[STRING_COOKIE], ptr);
case CURLOPT_COOKIEFILE:
/*
* Set cookie file to read and parse. Can be used multiple times.
*/
if(ptr) {
struct curl_slist *cl;
/* general protection against mistakes and abuse */
if(strlen(ptr) > CURL_MAX_INPUT_LENGTH)
return CURLE_BAD_FUNCTION_ARGUMENT;
/* append the cookie filename to the list of filenames, and deal with
them later */
cl = curl_slist_append(data->state.cookielist, ptr);
if(!cl) {
curl_slist_free_all(data->state.cookielist);
data->state.cookielist = NULL;
return CURLE_OUT_OF_MEMORY;
}
data->state.cookielist = cl; /* store the list for later use */
}
else {
/* clear the list of cookie files */
curl_slist_free_all(data->state.cookielist);
data->state.cookielist = NULL;
if(!data->share || !data->share->cookies) {
/* throw away all existing cookies if this is not a shared cookie
container */
Curl_cookie_clearall(data->cookies);
Curl_cookie_cleanup(data->cookies);
}
/* disable the cookie engine */
data->cookies = NULL;
}
break;
case CURLOPT_COOKIEJAR:
/*
* Set cookie filename to dump all cookies to when we are done.
*/
result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], ptr);
if(!result) {
/*
* Activate the cookie parser. This may or may not already
* have been made.
*/
struct CookieInfo *newcookies =
Curl_cookie_init(data, NULL, data->cookies, data->set.cookiesession);
if(!newcookies)
result = CURLE_OUT_OF_MEMORY;
data->cookies = newcookies;
}
break;
case CURLOPT_COOKIELIST:
if(!ptr)
break;
if(strcasecompare(ptr, "ALL")) {
/* clear all cookies */
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_clearall(data->cookies);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
else if(strcasecompare(ptr, "SESS")) {
/* clear session cookies */
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
Curl_cookie_clearsess(data->cookies);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
else if(strcasecompare(ptr, "FLUSH")) {
/* flush cookies to file, takes care of the locking */
Curl_flush_cookies(data, FALSE);
}
else if(strcasecompare(ptr, "RELOAD")) {
/* reload cookies from file */
Curl_cookie_loadfiles(data);
break;
}
else {
if(!data->cookies) {
/* if cookie engine was not running, activate it */
data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);
if(!data->cookies)
return CURLE_OUT_OF_MEMORY;
}
/* general protection against mistakes and abuse */
if(strlen(ptr) > CURL_MAX_INPUT_LENGTH)
return CURLE_BAD_FUNCTION_ARGUMENT;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
if(checkprefix("Set-Cookie:", ptr))
/* HTTP Header format line */
Curl_cookie_add(data, data->cookies, TRUE, FALSE, ptr + 11, NULL,
NULL, TRUE);
else
/* Netscape format line */
Curl_cookie_add(data, data->cookies, FALSE, FALSE, ptr, NULL,
NULL, TRUE);
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
break;
#endif /* !CURL_DISABLE_COOKIES */
#endif /* ! CURL_DISABLE_HTTP */
case CURLOPT_CUSTOMREQUEST:
/*
* Set a custom string to use as request
*/
return Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST], ptr);
/* we do not set
data->set.method = HTTPREQ_CUSTOM;
here, we continue as if we were using the already set type
and this just changes the actual request keyword */
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY:
/*
* Set proxy server:port to use as proxy.
*
* If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL)
* we explicitly say that we do not want to use a proxy
* (even though there might be environment variables saying so).
*
* Setting it to NULL, means no proxy but allows the environment variables
* to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL).
*/
return Curl_setstropt(&data->set.str[STRING_PROXY], ptr);
break;
case CURLOPT_PRE_PROXY:
/*
* Set proxy server:port to use as SOCKS proxy.
*
* If the proxy is set to "" or NULL we explicitly say that we do not want
* to use the socks proxy.
*/
return Curl_setstropt(&data->set.str[STRING_PRE_PROXY], ptr);
#endif /* CURL_DISABLE_PROXY */
#ifndef CURL_DISABLE_PROXY
case CURLOPT_SOCKS5_GSSAPI_SERVICE:
case CURLOPT_PROXY_SERVICE_NAME:
/*
* Set proxy authentication service name for Kerberos 5 and SPNEGO
*/
return Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], ptr);
#endif
case CURLOPT_SERVICE_NAME:
/*
* Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO
*/
return Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], ptr);
break;
case CURLOPT_HEADERDATA:
/*
* Custom pointer to pass the header write callback function
*/
data->set.writeheader = (void *)ptr;
break;
case CURLOPT_READDATA:
/*
* FILE pointer to read the file to be uploaded from. Or possibly used as
* argument to the read callback.
*/
data->set.in_set = (void *)ptr;
break;
case CURLOPT_WRITEDATA:
/*
* FILE pointer to write to. Or possibly used as argument to the write
* callback.
*/
data->set.out = (void *)ptr;
break;
case CURLOPT_DEBUGDATA:
/*
* Set to a void * that should receive all error writes. This
* defaults to CURLOPT_STDERR for normal operations.
*/
data->set.debugdata = (void *)ptr;
break;
case CURLOPT_PROGRESSDATA:
/*
* Custom client data to pass to the progress callback
*/
data->set.progress_client = (void *)ptr;
break;
case CURLOPT_SEEKDATA:
/*
* Seek control callback. Might be NULL.
*/
data->set.seek_client = (void *)ptr;
break;
case CURLOPT_IOCTLDATA:
/*
* I/O control data pointer. Might be NULL.
*/
data->set.ioctl_client = (void *)ptr;
break;
case CURLOPT_SSL_CTX_DATA:
/*
* Set a SSL_CTX callback parameter pointer
*/
#ifdef USE_SSL
if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX))
data->set.ssl.fsslctxp = (void *)ptr;
else
#endif
return CURLE_NOT_BUILT_IN;
break;
case CURLOPT_SOCKOPTDATA:
/*
* socket callback data pointer. Might be NULL.
*/
data->set.sockopt_client = (void *)ptr;
break;
case CURLOPT_OPENSOCKETDATA:
/*
* socket callback data pointer. Might be NULL.
*/
data->set.opensocket_client = (void *)ptr;
break;
case CURLOPT_RESOLVER_START_DATA:
/*
* resolver start callback data pointer. Might be NULL.
*/
data->set.resolver_start_client = (void *)ptr;
break;
case CURLOPT_CLOSESOCKETDATA:
/*
* socket callback data pointer. Might be NULL.
*/
data->set.closesocket_client = (void *)ptr;
break;
case CURLOPT_TRAILERDATA:
#ifndef CURL_DISABLE_HTTP
data->set.trailer_data = (void *)ptr;
#endif
break;
case CURLOPT_PREREQDATA:
data->set.prereq_userp = (void *)ptr;
break;
case CURLOPT_ERRORBUFFER:
/*
* Error buffer provided by the caller to get the human readable error
* string in.
*/
data->set.errorbuffer = ptr;
break;
#ifndef CURL_DISABLE_FTP
case CURLOPT_FTPPORT:
/*
* Use FTP PORT, this also specifies which IP address to use
*/
result = Curl_setstropt(&data->set.str[STRING_FTPPORT], ptr);
data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]);
break;
case CURLOPT_FTP_ACCOUNT:
return Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT], ptr);
case CURLOPT_FTP_ALTERNATIVE_TO_USER:
return Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], ptr);
#ifdef HAVE_GSSAPI
case CURLOPT_KRBLEVEL:
/*
* A string that defines the kerberos security level.
*/
result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], ptr);
data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]);
break;
#endif
#endif
case CURLOPT_URL:
/*
* The URL to fetch.
*/
if(data->state.url_alloc) {
/* the already set URL is allocated, free it first! */
Curl_safefree(data->state.url);
data->state.url_alloc = FALSE;
}
result = Curl_setstropt(&data->set.str[STRING_SET_URL], ptr);
data->state.url = data->set.str[STRING_SET_URL];
break;
case CURLOPT_USERPWD:
/*
* user:password to use in the operation
*/
return setstropt_userpwd(ptr, &data->set.str[STRING_USERNAME],
&data->set.str[STRING_PASSWORD]);
case CURLOPT_USERNAME:
/*
* authentication username to use in the operation
*/
return Curl_setstropt(&data->set.str[STRING_USERNAME], ptr);
case CURLOPT_PASSWORD:
/*
* authentication password to use in the operation
*/
return Curl_setstropt(&data->set.str[STRING_PASSWORD], ptr);
case CURLOPT_LOGIN_OPTIONS:
/*
* authentication options to use in the operation
*/
return Curl_setstropt(&data->set.str[STRING_OPTIONS], ptr);
case CURLOPT_XOAUTH2_BEARER:
/*
* OAuth 2.0 bearer token to use in the operation
*/
return Curl_setstropt(&data->set.str[STRING_BEARER], ptr);
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXYUSERPWD: {
/*
* user:password needed to use the proxy
*/
char *u = NULL;
char *p = NULL;
result = setstropt_userpwd(ptr, &u, &p);
/* URL decode the components */
if(!result && u)
result = Curl_urldecode(u, 0, &data->set.str[STRING_PROXYUSERNAME], NULL,
REJECT_ZERO);
if(!result && p)
result = Curl_urldecode(p, 0, &data->set.str[STRING_PROXYPASSWORD], NULL,
REJECT_ZERO);
free(u);
free(p);
}
break;
case CURLOPT_PROXYUSERNAME:
/*
* authentication username to use in the operation
*/
return Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME], ptr);
case CURLOPT_PROXYPASSWORD:
/*
* authentication password to use in the operation
*/
return Curl_setstropt(&data->set.str[STRING_PROXYPASSWORD], ptr);
case CURLOPT_NOPROXY:
/*
* proxy exception list
*/
return Curl_setstropt(&data->set.str[STRING_NOPROXY], ptr);
#endif
case CURLOPT_RANGE:
/*
* What range of the file you want to transfer
*/
return Curl_setstropt(&data->set.str[STRING_SET_RANGE], ptr);
#endif /* ! CURL_DISABLE_PROXY */

For builds with HTTP and MQTT disabled,
CURLOPT_URL, CURLOPT_READDATA, CURLOPT_WRITEDATA, etc. are also disabled.

I expected the following

curl_easy_setopt should return CURLE_OK(zero).

curl/libcurl version

curl 8.11.0 (i386-pc-win32) libcurl/8.11.0 OpenSSL/1.1.1w
Release-Date: 2024-11-06
Protocols: ftp ftps
Features: AsynchDNS Largefile SSL threadsafe UnixSockets

We use our own built module for FTP transfers.
Problem did not reproduce when building curl 8.10.1 source

operating system

Windows 10 22H2 OS Build 19045.5011

@jay
Copy link
Member

jay commented Nov 26, 2024

Please try #15640

jay added a commit to jay/curl that referenced this issue Nov 26, 2024
- Restore some necessary options for builds without HTTP and MQTT.

The logic to turn off a segment of options in builds without HTTP and
MQTT was too expansive. Those builds (such as FTP-only builds) could not
use options such as CURLOPT_URL or CURLOPT_USERNAME etc.

Prior to this change 30da1f5 (precedes 8.11.0) refactored the options
parsing and caused this issue.

Reported-by: Yoshimasa Ohno

Fixes curl#15634
Closes #xxxx
@momontyo
Copy link
Author

@jay
Thanks for providing the pullrequest.
I immediately applied the lib/setopt.c patch to the 8.11.0 source and built it, and confirmed that the title issue is resolved.
I have not checked all the options, but I can confirm that FTP Download/Upload/Wildcard(Listing) works.

@jay jay closed this as completed in b1c54e1 Nov 29, 2024
@jay
Copy link
Member

jay commented Nov 29, 2024

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

4 participants