ftp: split ftp_state_use_port into sub functions

For readability and reduced complexity.

Fixed a bug when FTPPORT specifies an IPv6-address only, without brackets.

Closes #20685
This commit is contained in:
Daniel Stenberg 2026-02-22 16:55:55 +01:00
parent f1cee1f18a
commit 29bca12978
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

572
lib/ftp.c
View file

@ -873,199 +873,228 @@ typedef enum {
DONE DONE
} ftpport; } ftpport;
static CURLcode ftp_state_use_port(struct Curl_easy *data, /*
struct ftp_conn *ftpc, * Parse the CURLOPT_FTPPORT string
ftpport fcmd) /* start with this */ * "(ipv4|ipv6|domain|interface)?(:port(-range)?)?"
* and extract addr/addrlen and port_min/port_max.
*/
static CURLcode ftp_port_parse_string(struct Curl_easy *data,
struct connectdata *conn,
const char *string_ftpport,
struct Curl_sockaddr_storage *ss,
unsigned short *port_minp,
unsigned short *port_maxp,
const char **hostp,
char *hbuf, size_t hbuflen)
{ {
CURLcode result = CURLE_FTP_PORT_FAILED; const char *ip_end = NULL;
struct connectdata *conn = data->conn; const char *addr = NULL;
curl_socket_t portsock = CURL_SOCKET_BAD; size_t addrlen = 0;
char myhost[MAX_IPADR_LEN + 1] = ""; unsigned short port_min = 0;
unsigned short port_max = 0;
char ipstr[50];
#ifndef USE_IPV6
(void)conn;
(void)ss;
#endif
struct Curl_sockaddr_storage ss; /* default to nothing */
const struct Curl_addrinfo *res, *ai; *hostp = NULL;
curl_socklen_t sslen; *port_minp = *port_maxp = 0;
char hbuf[NI_MAXHOST];
struct sockaddr *sa = (struct sockaddr *)&ss; if(!string_ftpport || (strlen(string_ftpport) <= 1))
goto done;
#ifdef USE_IPV6
if(*string_ftpport == '[') {
/* [ipv6]:port(-range) */
const char *ip_start = string_ftpport + 1;
ip_end = strchr(ip_start, ']');
if(ip_end) {
addrlen = ip_end - ip_start;
addr = ip_start;
}
}
else
#endif
if(*string_ftpport == ':') {
/* :port */
ip_end = string_ftpport;
}
else {
ip_end = strchr(string_ftpport, ':');
addr = string_ftpport;
if(ip_end) {
#ifdef USE_IPV6
struct sockaddr_in6 * const sa6 = (void *)ss;
#endif
/* either ipv6 or (ipv4|domain|interface):port(-range) */
addrlen = ip_end - string_ftpport;
#ifdef USE_IPV6
if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
/* ipv6 */
port_min = port_max = 0;
addrlen = strlen(string_ftpport);
ip_end = NULL; /* this got no port ! */
}
#endif
}
else
/* ipv4|interface */
addrlen = strlen(string_ftpport);
}
/* parse the port */
if(ip_end) {
const char *portp = strchr(ip_end, ':');
if(portp) {
curl_off_t start;
curl_off_t end;
portp++;
if(!curlx_str_number(&portp, &start, 0xffff)) {
port_min = (unsigned short)start;
if(!curlx_str_single(&portp, '-') &&
!curlx_str_number(&portp, &end, 0xffff))
port_max = (unsigned short)end;
else
port_max = port_min;
}
else
port_max = port_min;
}
}
/* correct errors like :1234-1230 or :-4711 */
if(port_min > port_max)
port_min = port_max = 0;
if(addrlen) {
const struct Curl_sockaddr_ex *remote_addr =
Curl_conn_get_remote_addr(data, FIRSTSOCKET);
DEBUGASSERT(remote_addr);
DEBUGASSERT(addr);
if(!remote_addr || (addrlen >= sizeof(ipstr)) || (addrlen >= hbuflen))
return CURLE_FTP_PORT_FAILED;
memcpy(ipstr, addr, addrlen);
ipstr[addrlen] = 0;
/* attempt to get the address of the given interface name */
switch(Curl_if2ip(remote_addr->family,
#ifdef USE_IPV6
Curl_ipv6_scope(&remote_addr->curl_sa_addr),
conn->scope_id,
#endif
ipstr, hbuf, hbuflen)) {
case IF2IP_NOT_FOUND:
/* not an interface, use the string as hostname instead */
memcpy(hbuf, addr, addrlen);
hbuf[addrlen] = 0;
*hostp = hbuf;
break;
case IF2IP_AF_NOT_SUPPORTED:
return CURLE_FTP_PORT_FAILED;
case IF2IP_FOUND:
*hostp = hbuf; /* use the hbuf for hostname */
break;
}
}
/* else: only a port(-range) given, leave host as NULL */
done:
*port_minp = port_min;
*port_maxp = port_max;
return CURLE_OK;
}
/*
* If no host was derived from the FTPPORT string, fall back to the IP address
* of the control connection's local socket.
*/
static CURLcode ftp_port_default_host(struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_storage *ss,
curl_socklen_t *sslenp,
const char **hostp,
char *hbuf, size_t hbuflen,
bool *non_localp)
{
struct sockaddr *sa = (struct sockaddr *)ss;
struct sockaddr_in * const sa4 = (void *)sa; struct sockaddr_in * const sa4 = (void *)sa;
#ifdef USE_IPV6 #ifdef USE_IPV6
struct sockaddr_in6 * const sa6 = (void *)sa; struct sockaddr_in6 * const sa6 = (void *)sa;
#endif #endif
static const char mode[][5] = { "EPRT", "PORT" };
int error;
const char *host = NULL;
const char *string_ftpport = data->set.str[STRING_FTPPORT];
struct Curl_dns_entry *dns_entry = NULL;
unsigned short port_min = 0;
unsigned short port_max = 0;
unsigned short port;
bool possibly_non_local = TRUE;
char buffer[STRERROR_LEN]; char buffer[STRERROR_LEN];
const char *addr = NULL; const char *r;
size_t addrlen = 0;
char ipstr[50];
/* Step 1, figure out what is requested, *sslenp = sizeof(*ss);
* accepted format : if(getsockname(conn->sock[FIRSTSOCKET], sa, sslenp)) {
* (ipv4|ipv6|domain|interface)?(:port(-range)?)? failf(data, "getsockname() failed: %s",
*/ curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
return CURLE_FTP_PORT_FAILED;
if(data->set.str[STRING_FTPPORT] &&
(strlen(data->set.str[STRING_FTPPORT]) > 1)) {
const char *ip_end = NULL;
#ifdef USE_IPV6
if(*string_ftpport == '[') {
/* [ipv6]:port(-range) */
const char *ip_start = string_ftpport + 1;
ip_end = strchr(ip_start, ']');
if(ip_end) {
addrlen = ip_end - ip_start;
addr = ip_start;
}
}
else
#endif
if(*string_ftpport == ':') {
/* :port */
ip_end = string_ftpport;
}
else {
ip_end = strchr(string_ftpport, ':');
addr = string_ftpport;
if(ip_end) {
/* either ipv6 or (ipv4|domain|interface):port(-range) */
addrlen = ip_end - string_ftpport;
#ifdef USE_IPV6
if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
/* ipv6 */
port_min = port_max = 0;
ip_end = NULL; /* this got no port ! */
}
#endif
}
else
/* ipv4|interface */
addrlen = strlen(string_ftpport);
}
/* parse the port */
if(ip_end) {
const char *portp = strchr(ip_end, ':');
if(portp) {
curl_off_t start;
curl_off_t end;
portp++;
if(!curlx_str_number(&portp, &start, 0xffff)) {
/* got the first number */
port_min = (unsigned short)start;
if(!curlx_str_single(&portp, '-')) {
/* got the dash */
if(!curlx_str_number(&portp, &end, 0xffff))
/* got the second number */
port_max = (unsigned short)end;
}
}
else
port_max = port_min;
}
}
/* correct errors like:
* :1234-1230
* :-4711, in this case port_min is (unsigned)-1,
* therefore port_min > port_max for all cases
* but port_max = (unsigned)-1
*/
if(port_min > port_max)
port_min = port_max = 0;
if(addrlen) {
const struct Curl_sockaddr_ex *remote_addr =
Curl_conn_get_remote_addr(data, FIRSTSOCKET);
DEBUGASSERT(remote_addr);
if(!remote_addr)
goto out;
DEBUGASSERT(addr);
if(addrlen >= sizeof(ipstr))
goto out;
memcpy(ipstr, addr, addrlen);
ipstr[addrlen] = 0;
/* attempt to get the address of the given interface name */
switch(Curl_if2ip(remote_addr->family,
#ifdef USE_IPV6
Curl_ipv6_scope(&remote_addr->curl_sa_addr),
conn->scope_id,
#endif
ipstr, hbuf, sizeof(hbuf))) {
case IF2IP_NOT_FOUND:
/* not an interface, use the given string as hostname instead */
host = ipstr;
break;
case IF2IP_AF_NOT_SUPPORTED:
goto out;
case IF2IP_FOUND:
host = hbuf; /* use the hbuf for hostname */
break;
}
}
else
/* there was only a port(-range) given, default the host */
host = NULL;
} /* data->set.ftpport */
if(!host) {
const char *r;
/* not an interface and not a hostname, get default by extracting
the IP from the control connection */
sslen = sizeof(ss);
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
goto out;
}
switch(sa->sa_family) {
#ifdef USE_IPV6
case AF_INET6:
r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
break;
#endif
default:
r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
break;
}
if(!r) {
goto out;
}
host = hbuf; /* use this hostname */
possibly_non_local = FALSE; /* we know it is local now */
} }
switch(sa->sa_family) {
/* resolv ip/host to ip */ #ifdef USE_IPV6
res = NULL; case AF_INET6:
result = Curl_resolv_blocking(data, host, 0, conn->ip_version, &dns_entry); r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, hbuflen);
if(!result) { break;
DEBUGASSERT(dns_entry); #endif
res = dns_entry->addr; default:
r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, hbuflen);
break;
} }
if(!r)
return CURLE_FTP_PORT_FAILED;
if(!res) { *hostp = hbuf;
*non_localp = FALSE; /* we know it is local now */
return CURLE_OK;
}
/*
* Resolve the host string to a list of addresses.
*/
static CURLcode ftp_port_resolve_host(struct Curl_easy *data,
struct connectdata *conn,
const char *host,
struct Curl_dns_entry **dns_entryp,
const struct Curl_addrinfo **resp)
{
CURLcode result;
*resp = NULL;
result = Curl_resolv_blocking(data, host, 0, conn->ip_version,
dns_entryp);
if(result)
failf(data, "failed to resolve the address provided to PORT: %s", host); failf(data, "failed to resolve the address provided to PORT: %s", host);
goto out; else {
DEBUGASSERT(*dns_entryp);
*resp = (*dns_entryp)->addr;
} }
return result;
}
host = NULL; /*
* Open a TCP socket for the resolved address family.
*/
static CURLcode ftp_port_open_socket(struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *res,
const struct Curl_addrinfo **aip,
curl_socket_t *portsockp)
{
char buffer[STRERROR_LEN];
int error = 0;
const struct Curl_addrinfo *ai;
CURLcode result = CURLE_FTP_PORT_FAILED;
/* step 2, create a socket for the requested address */
error = 0;
for(ai = res; ai; ai = ai->ai_next) { for(ai = res; ai; ai = ai->ai_next) {
result = Curl_socket_open(data, ai, NULL, result =
Curl_conn_get_transport(data, conn), &portsock); Curl_socket_open(data, ai, NULL,
Curl_conn_get_transport(data, conn), portsockp);
if(result) { if(result) {
if(result == CURLE_OUT_OF_MEMORY) if(result == CURLE_OUT_OF_MEMORY)
goto out; return result;
result = CURLE_FTP_PORT_FAILED; result = CURLE_FTP_PORT_FAILED;
error = SOCKERRNO; error = SOCKERRNO;
continue; continue;
@ -1075,15 +1104,38 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(!ai) { if(!ai) {
failf(data, "socket failure: %s", failf(data, "socket failure: %s",
curlx_strerror(error, buffer, sizeof(buffer))); curlx_strerror(error, buffer, sizeof(buffer)));
goto out; return CURLE_FTP_PORT_FAILED;
} }
CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket", *aip = ai;
FTP_CSTATE(ftpc)); return result;
}
/* step 3, bind to a suitable local address */ /*
* Bind the socket to a local address and port within the requested range.
* Falls back to the control-connection address if the user-requested address
* is non-local.
*/
static CURLcode ftp_port_bind_socket(struct Curl_easy *data,
struct connectdata *conn,
curl_socket_t portsock,
const struct Curl_addrinfo *ai,
struct Curl_sockaddr_storage *ss,
curl_socklen_t *sslen_io,
unsigned short port_min,
unsigned short port_max,
bool non_local)
{
struct sockaddr *sa = (struct sockaddr *)ss;
struct sockaddr_in * const sa4 = (void *)sa;
#ifdef USE_IPV6
struct sockaddr_in6 * const sa6 = (void *)sa;
#endif
char buffer[STRERROR_LEN];
unsigned short port;
int error;
memcpy(sa, ai->ai_addr, ai->ai_addrlen); memcpy(sa, ai->ai_addr, ai->ai_addrlen);
sslen = ai->ai_addrlen; *sslen_io = ai->ai_addrlen;
for(port = port_min; port <= port_max;) { for(port = port_min; port <= port_max;) {
if(sa->sa_family == AF_INET) if(sa->sa_family == AF_INET)
@ -1092,71 +1144,97 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
else else
sa6->sin6_port = htons(port); sa6->sin6_port = htons(port);
#endif #endif
/* Try binding the given address. */ if(bind(portsock, sa, *sslen_io)) {
if(bind(portsock, sa, sslen)) {
/* It failed. */
error = SOCKERRNO; error = SOCKERRNO;
if(possibly_non_local && (error == SOCKEADDRNOTAVAIL)) { if(non_local && (error == SOCKEADDRNOTAVAIL)) {
/* The requested bind address is not local. Use the address used for /* The requested bind address is not local. Use the address used for
* the control connection instead and restart the port loop * the control connection instead and restart the port loop.
*/ */
infof(data, "bind(port=%hu) on non-local address failed: %s", port, infof(data, "bind(port=%hu) on non-local address failed: %s", port,
curlx_strerror(error, buffer, sizeof(buffer))); curlx_strerror(error, buffer, sizeof(buffer)));
sslen = sizeof(ss); *sslen_io = sizeof(*ss);
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) { if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) {
failf(data, "getsockname() failed: %s", failf(data, "getsockname() failed: %s",
curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
goto out; return CURLE_FTP_PORT_FAILED;
} }
port = port_min; port = port_min;
possibly_non_local = FALSE; /* do not try this again */ non_local = FALSE; /* do not try this again */
continue; continue;
} }
if(error != SOCKEADDRINUSE && error != SOCKEACCES) { if(error != SOCKEADDRINUSE && error != SOCKEACCES) {
failf(data, "bind(port=%hu) failed: %s", port, failf(data, "bind(port=%hu) failed: %s", port,
curlx_strerror(error, buffer, sizeof(buffer))); curlx_strerror(error, buffer, sizeof(buffer)));
goto out; return CURLE_FTP_PORT_FAILED;
} }
} }
else else
break; break;
/* check if port is the maximum value here, because it might be 0xffff and /* check if port is the maximum value here, because it might be 0xffff
then the increment below will wrap the 16-bit counter */ and then the increment below will wrap the 16-bit counter */
if(port == port_max) { if(port == port_max) {
/* maybe all ports were in use already */
failf(data, "bind() failed, ran out of ports"); failf(data, "bind() failed, ran out of ports");
goto out; return CURLE_FTP_PORT_FAILED;
} }
port++; port++;
} }
/* get the name again after the bind() so that we can extract the /* re-read the name so we can extract the actual port chosen */
port number it uses now */ *sslen_io = sizeof(*ss);
sslen = sizeof(ss); if(getsockname(portsock, sa, sslen_io)) {
if(getsockname(portsock, sa, &sslen)) {
failf(data, "getsockname() failed: %s", failf(data, "getsockname() failed: %s",
curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
goto out; return CURLE_FTP_PORT_FAILED;
} }
CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d", CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d",
FTP_CSTATE(ftpc), port); port);
return CURLE_OK;
}
/* step 4, listen on the socket */ /*
* Start listening on the data socket.
*/
static CURLcode ftp_port_listen(struct Curl_easy *data, curl_socket_t portsock)
{
char buffer[STRERROR_LEN];
if(listen(portsock, 1)) { if(listen(portsock, 1)) {
failf(data, "socket failure: %s", failf(data, "socket failure: %s",
curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
goto out; return CURLE_FTP_PORT_FAILED;
} }
CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d", CURL_TRC_FTP(data, "ftp_port_listen(), listening on port");
FTP_CSTATE(ftpc), port); return CURLE_OK;
}
/* step 5, send the proper FTP command */ /*
* Send the EPRT or PORT command to the server.
*/
static CURLcode ftp_port_send_command(struct Curl_easy *data,
struct ftp_conn *ftpc,
struct connectdata *conn,
struct Curl_sockaddr_storage *ss,
const struct Curl_addrinfo *ai,
ftpport fcmd)
{
static const char mode[][5] = { "EPRT", "PORT" };
struct sockaddr *sa = (struct sockaddr *)ss;
struct sockaddr_in * const sa4 = (void *)sa;
#ifdef USE_IPV6
struct sockaddr_in6 * const sa6 = (void *)sa;
#endif
char myhost[MAX_IPADR_LEN + 1] = "";
unsigned short port;
CURLcode result;
/* get a plain printable version of the numerical address to work with /* Get a plain printable version of the numerical address to work with. This
below */ logic uses the address provided by the FTPPORT option, which at times
might differ from the address in 'ss' used to bind to: when a user asks
the server to connect to a specific address knowing that it works, but
curl instead selects to listen to the local address because it cannot use
the provided address. FTP is strange. */
Curl_printable_address(ai, myhost, sizeof(myhost)); Curl_printable_address(ai, myhost, sizeof(myhost));
#ifdef USE_IPV6 #ifdef USE_IPV6
@ -1197,14 +1275,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
* *
* EPRT |2|1080::8:800:200C:417A|5282| * EPRT |2|1080::8:800:200C:417A|5282|
*/ */
result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd], result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
sa->sa_family == AF_INET ? 1 : 2, sa->sa_family == AF_INET ? 1 : 2,
myhost, port); myhost, port);
if(result) { if(result) {
failf(data, "Failure sending EPRT command: %s", failf(data, "Failure sending EPRT command: %s",
curl_easy_strerror(result)); curl_easy_strerror(result));
goto out; return result;
} }
break; break;
} }
@ -1230,7 +1307,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(result) { if(result) {
failf(data, "Failure sending PORT command: %s", failf(data, "Failure sending PORT command: %s",
curl_easy_strerror(result)); curl_easy_strerror(result));
goto out; return result;
} }
break; break;
} }
@ -1239,21 +1316,84 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* store which command was sent */ /* store which command was sent */
ftpc->count1 = fcmd; ftpc->count1 = fcmd;
ftp_state(data, ftpc, FTP_PORT); ftp_state(data, ftpc, FTP_PORT);
return CURLE_OK;
}
/*
* ftp_state_use_port()
*
* Set up an active-mode FTP data connection (using PORT or EPRT) and start
* listening for the server's incoming connection on SECONDARYSOCKET.
*/
static CURLcode ftp_state_use_port(struct Curl_easy *data,
struct ftp_conn *ftpc,
ftpport fcmd) /* start with this */
{
CURLcode result = CURLE_FTP_PORT_FAILED;
struct connectdata *conn = data->conn;
curl_socket_t portsock = CURL_SOCKET_BAD;
struct Curl_sockaddr_storage ss;
curl_socklen_t sslen;
char hbuf[NI_MAXHOST];
const char *host = NULL;
const char *string_ftpport = data->set.str[STRING_FTPPORT];
struct Curl_dns_entry *dns_entry = NULL;
const struct Curl_addrinfo *res = NULL;
const struct Curl_addrinfo *ai = NULL;
unsigned short port_min = 0;
unsigned short port_max = 0;
bool non_local = TRUE;
/* parse the FTPPORT string for address and port range */
result = ftp_port_parse_string(data, conn, string_ftpport,
&ss, &port_min, &port_max,
&host, hbuf, sizeof(hbuf));
if(!result && !host)
/* if no host was specified, use the control connection's local IP */
result = ftp_port_default_host(data, conn, &ss, &sslen, &host,
hbuf, sizeof(hbuf), &non_local);
/* resolve host string to address list */
if(!result)
result = ftp_port_resolve_host(data, conn, host, &dns_entry, &res);
/* Open a TCP socket for the data connection */
if(!result)
result = ftp_port_open_socket(data, conn, res, &ai, &portsock);
if(!result) {
CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket",
FTP_CSTATE(ftpc));
/* bind to a suitable local address / port */
result = ftp_port_bind_socket(data, conn, portsock, ai, &ss, &sslen,
port_min, port_max, non_local);
}
/* listen */
if(!result)
result = ftp_port_listen(data, portsock);
/* send the PORT / EPRT command */
if(!result)
result = ftp_port_send_command(data, ftpc, conn, &ss, ai, fcmd);
/* replace any filter on SECONDARY with one listening on this socket */
if(!result)
result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
/* Replace any filter on SECONDARY with one listening on this socket */
result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
if(!result) if(!result)
portsock = CURL_SOCKET_BAD; /* now held in filter */ portsock = CURL_SOCKET_BAD; /* now held in filter */
out: /* cleanup */
/* If we looked up a dns_entry, now is the time to safely release it */
if(dns_entry) if(dns_entry)
Curl_resolv_unlink(data, &dns_entry); Curl_resolv_unlink(data, &dns_entry);
if(result) { if(result) {
ftp_state(data, ftpc, FTP_STOP); ftp_state(data, ftpc, FTP_STOP);
} }
else { else {
/* successfully setup the list socket filter. Do we need more? */ /* successfully set up the listen socket filter. SSL needed? */
if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
!Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);