summaryrefslogtreecommitdiff
path: root/lib/ftp.c
diff options
context:
space:
mode:
authorStefan Eissing <stefan@eissing.org>2022-11-11 11:45:34 +0100
committerDaniel Stenberg <daniel@haxx.se>2022-11-11 15:17:51 +0100
commitdafdb20a26d0c890e83dea61a104b75408481ebd (patch)
tree40824f46de18cb7b7b47fb06a3be624c9c06961b /lib/ftp.c
parent89ee5cfb38b22f9ff68c34aa55ca2c242be90826 (diff)
downloadcurl-dafdb20a26d0c890e83dea61a104b75408481ebd.tar.gz
lib: connection filters (cfilter) addition to curl:
- general construct/destroy in connectdata - default implementations of callback functions - connect: cfilters for connect and accept - socks: cfilter for socks proxying - http_proxy: cfilter for http proxy tunneling - vtls: cfilters for primary and proxy ssl - change in general handling of data/conn - Curl_cfilter_setup() sets up filter chain based on data settings, if none are installed by the protocol handler setup - Curl_cfilter_connect() boot straps filters into `connected` status, used by handlers and multi to reach further stages - Curl_cfilter_is_connected() to check if a conn is connected, e.g. all filters have done their work - Curl_cfilter_get_select_socks() gets the sockets and READ/WRITE indicators for multi select to work - Curl_cfilter_data_pending() asks filters if the have incoming data pending for recv - Curl_cfilter_recv()/Curl_cfilter_send are the general callbacks installed in conn->recv/conn->send for io handling - Curl_cfilter_attach_data()/Curl_cfilter_detach_data() inform filters and addition/removal of a `data` from their connection - adding vtl functions to prevent use of Curl_ssl globals directly in other parts of the code. Reviewed-by: Daniel Stenberg Closes #9855
Diffstat (limited to 'lib/ftp.c')
-rw-r--r--lib/ftp.c202
1 files changed, 80 insertions, 122 deletions
diff --git a/lib/ftp.c b/lib/ftp.c
index 4d3bcc387..b85a27493 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -65,6 +65,7 @@
#include "strtoofft.h"
#include "strcase.h"
#include "vtls/vtls.h"
+#include "cfilters.h"
#include "connect.h"
#include "strerror.h"
#include "inet_ntop.h"
@@ -218,14 +219,8 @@ const struct Curl_handler Curl_handler_ftps = {
static void close_secondarysocket(struct Curl_easy *data,
struct connectdata *conn)
{
- if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
- Curl_closesocket(data, conn, conn->sock[SECONDARYSOCKET]);
- conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
- }
- conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
-#ifndef CURL_DISABLE_PROXY
- conn->bits.proxy_ssl_connected[SECONDARYSOCKET] = FALSE;
-#endif
+ Curl_cfilter_close(data, conn, SECONDARYSOCKET);
+ Curl_cfilter_destroy(data, conn, SECONDARYSOCKET);
}
/*
@@ -277,13 +272,13 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
struct sockaddr_in add;
#endif
curl_socklen_t size = (curl_socklen_t) sizeof(add);
+ CURLcode result;
if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
size = sizeof(add);
s = accept(sock, (struct sockaddr *) &add, &size);
}
- Curl_closesocket(data, conn, sock); /* close the first socket */
if(CURL_SOCKET_BAD == s) {
failf(data, "Error accept()ing server connect");
@@ -294,9 +289,11 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
not needing DO_MORE anymore */
conn->bits.do_more = FALSE;
- conn->sock[SECONDARYSOCKET] = s;
(void)curlx_nonblock(s, TRUE); /* enable non-blocking */
- conn->bits.sock_accepted = TRUE;
+ /* Replace any filter on SECONDARY with one listeing on this socket */
+ result = Curl_cfilter_socket_accepted_set(data, conn, SECONDARYSOCKET, &s);
+ if(result)
+ return result;
if(data->set.fsockopt) {
int error = 0;
@@ -440,15 +437,13 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
+ bool connected;
- if(conn->bits.ftp_use_data_ssl) {
- /* since we only have a plaintext TCP connection here, we must now
- * do the TLS stuff */
- infof(data, "Doing the SSL/TLS handshake on the data stream");
- result = Curl_ssl_connect(data, conn, SECONDARYSOCKET);
- if(result)
- return result;
- }
+ DEBUGF(infof(data, "ftp InitiateTransfer()"));
+ result = Curl_cfilter_connect(data, conn, SECONDARYSOCKET,
+ TRUE, &connected);
+ if(result || !connected)
+ return result;
if(conn->proto.ftpc.state_saved == FTP_STOR) {
/* When we know we're uploading a specified file, we can get the file
@@ -496,22 +491,23 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
if(timeout_ms < 0) {
/* if a timeout was already reached, bail out */
failf(data, "Accept timeout occurred while waiting server connect");
- return CURLE_FTP_ACCEPT_TIMEOUT;
+ result = CURLE_FTP_ACCEPT_TIMEOUT;
+ goto out;
}
/* see if the connection request is already here */
result = ReceivedServerConnect(data, connected);
if(result)
- return result;
+ goto out;
if(*connected) {
result = AcceptServerConnect(data);
if(result)
- return result;
+ goto out;
result = InitiateTransfer(data);
if(result)
- return result;
+ goto out;
}
else {
/* Add timeout to multi handle and break out of the loop */
@@ -520,6 +516,8 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
EXPIRE_FTP_ACCEPT);
}
+out:
+ DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result));
return result;
}
@@ -671,7 +669,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
* wait for more data anyway.
*/
}
- else if(!Curl_conn_data_pending(conn, FIRSTSOCKET)) {
+ else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) {
switch(SOCKET_READABLE(sockfd, interval_ms)) {
case -1: /* select() error, stop reading */
failf(data, "FTP response aborted due to select/poll error: %d",
@@ -820,8 +818,10 @@ static int ftp_domore_getsock(struct Curl_easy *data,
* handle ordinary commands.
*/
- if(SOCKS_STATE(conn->cnnct.state))
- return Curl_SOCKS_getsock(conn, socks, SECONDARYSOCKET);
+ DEBUGF(infof(data, "ftp_domore_getsock()"));
+ if(conn->cfilter[SECONDARYSOCKET]
+ && !Curl_cfilter_is_connected(data, conn, SECONDARYSOCKET))
+ return Curl_cfilter_get_select_socks(data, conn, SECONDARYSOCKET, socks);
if(FTP_STOP == ftpc->state) {
int bits = GETSOCK_READSOCK(0);
@@ -916,7 +916,7 @@ typedef enum {
static CURLcode ftp_state_use_port(struct Curl_easy *data,
ftpport fcmd) /* start with this */
{
- CURLcode result = CURLE_OK;
+ CURLcode result = CURLE_FTP_PORT_FAILED;
struct connectdata *conn = data->conn;
struct ftp_conn *ftpc = &conn->proto.ftpc;
curl_socket_t portsock = CURL_SOCKET_BAD;
@@ -965,8 +965,10 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
char *port_sep = NULL;
addr = calloc(addrlen + 1, 1);
- if(!addr)
- return CURLE_OUT_OF_MEMORY;
+ if(!addr) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
#ifdef ENABLE_IPV6
if(*string_ftpport == '[') {
@@ -1026,7 +1028,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(port_min > port_max)
port_min = port_max = 0;
-
if(*addr != '\0') {
/* attempt to get the address of the given interface name */
switch(Curl_if2ip(conn->ip_addr->ai_family,
@@ -1040,7 +1041,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
host = addr;
break;
case IF2IP_AF_NOT_SUPPORTED:
- return CURLE_FTP_PORT_FAILED;
+ goto out;
case IF2IP_FOUND:
host = hbuf; /* use the hbuf for host name */
}
@@ -1058,8 +1059,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- free(addr);
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
switch(sa->sa_family) {
#ifdef ENABLE_IPV6
@@ -1071,8 +1071,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
break;
}
- if(!r)
- return CURLE_FTP_PORT_FAILED;
+ if(!r) {
+ goto out;
+ }
host = hbuf; /* use this host name */
possibly_non_local = FALSE; /* we know it is local now */
}
@@ -1092,11 +1093,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(!res) {
failf(data, "failed to resolve the address provided to PORT: %s", host);
- free(addr);
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
- free(addr);
host = NULL;
/* step 2, create a socket for the requested address */
@@ -1104,8 +1103,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
portsock = CURL_SOCKET_BAD;
error = 0;
for(ai = res; ai; ai = ai->ai_next) {
- result = Curl_socket(data, ai, NULL, &portsock);
- if(result) {
+ if(Curl_socket(data, ai, NULL, &portsock)) {
error = SOCKERRNO;
continue;
}
@@ -1114,8 +1112,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(!ai) {
failf(data, "socket failure: %s",
Curl_strerror(error, buffer, sizeof(buffer)));
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
+ DEBUGF(infof(data, "ftp_state_use_port(), opened socket"));
/* step 3, bind to a suitable local address */
@@ -1144,8 +1143,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- Curl_closesocket(data, conn, portsock);
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
port = port_min;
possibly_non_local = FALSE; /* don't try this again */
@@ -1154,8 +1152,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(error != EADDRINUSE && error != EACCES) {
failf(data, "bind(port=%hu) failed: %s", port,
Curl_strerror(error, buffer, sizeof(buffer)));
- Curl_closesocket(data, conn, portsock);
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
}
else
@@ -1167,8 +1164,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* maybe all ports were in use already */
if(port > port_max) {
failf(data, "bind() failed, we ran out of ports");
- Curl_closesocket(data, conn, portsock);
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
/* get the name again after the bind() so that we can extract the
@@ -1177,18 +1173,18 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(getsockname(portsock, sa, &sslen)) {
failf(data, "getsockname() failed: %s",
Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- Curl_closesocket(data, conn, portsock);
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
+ DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port));
/* step 4, listen on the socket */
if(listen(portsock, 1)) {
failf(data, "socket failure: %s",
Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
- Curl_closesocket(data, conn, portsock);
- return CURLE_FTP_PORT_FAILED;
+ goto out;
}
+ DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port));
/* step 5, send the proper FTP command */
@@ -1241,12 +1237,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(result) {
failf(data, "Failure sending EPRT command: %s",
curl_easy_strerror(result));
- Curl_closesocket(data, conn, portsock);
- /* don't retry using PORT */
- ftpc->count1 = PORT;
- /* bail out */
- state(data, FTP_STOP);
- return result;
+ goto out;
}
break;
}
@@ -1272,10 +1263,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(result) {
failf(data, "Failure sending PORT command: %s",
curl_easy_strerror(result));
- Curl_closesocket(data, conn, portsock);
- /* bail out */
- state(data, FTP_STOP);
- return result;
+ goto out;
}
break;
}
@@ -1284,23 +1272,21 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* store which command was sent */
ftpc->count1 = fcmd;
- close_secondarysocket(data, conn);
-
- /* we set the secondary socket variable to this for now, it is only so that
- the cleanup function will close it in case we fail before the true
- secondary stuff is made */
- conn->sock[SECONDARYSOCKET] = portsock;
-
- /* this tcpconnect assignment below is a hackish work-around to make the
- multi interface with active FTP work - as it will not wait for a
- (passive) connect in Curl_is_connected().
-
- The *proper* fix is to make sure that the active connection from the
- server is done in a non-blocking way. Currently, it is still BLOCKING.
- */
- conn->bits.tcpconnect[SECONDARYSOCKET] = TRUE;
-
+ /* Replace any filter on SECONDARY with one listeing on this socket */
+ result = Curl_cfilter_socket_accepted_set(data, conn, SECONDARYSOCKET,
+ &portsock);
+ if(result)
+ goto out;
+ portsock = CURL_SOCKET_BAD; /* now held in filter */
state(data, FTP_PORT);
+
+out:
+ if(result) {
+ state(data, FTP_STOP);
+ }
+ if(portsock != CURL_SOCKET_BAD)
+ Curl_closesocket(data, conn, portsock);
+ free(addr);
return result;
}
@@ -1803,6 +1789,8 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
infof(data, "Failed EPSV attempt. Disabling EPSV");
/* disable it for next transfer */
conn->bits.ftp_use_epsv = FALSE;
+ Curl_cfilter_close(data, conn, SECONDARYSOCKET);
+ Curl_cfilter_destroy(data, conn, SECONDARYSOCKET);
data->state.errorbuf = FALSE; /* allow error message to get
rewritten */
result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
@@ -1992,8 +1980,9 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
}
}
- conn->bits.tcpconnect[SECONDARYSOCKET] = FALSE;
- result = Curl_connecthost(data, conn, addr);
+ result = Curl_cfilter_setup(data, conn, SECONDARYSOCKET, addr,
+ conn->bits.ftp_use_data_ssl?
+ CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
if(result) {
Curl_resolv_unlock(data, addr); /* we're done using this address */
@@ -2209,6 +2198,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
struct connectdata *conn = data->conn;
struct ftp_conn *ftpc = &conn->proto.ftpc;
+ DEBUGF(infof(data, "ftp_state_retr()"));
if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
failf(data, "Maximum file size exceeded");
return CURLE_FILESIZE_EXCEEDED;
@@ -2755,8 +2745,9 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
*/
if((ftpcode == 234) || (ftpcode == 334)) {
- /* Curl_ssl_connect is BLOCKING */
- result = Curl_ssl_connect(data, conn, FIRSTSOCKET);
+ /* this was BLOCKING, keep it so for now */
+ bool done;
+ result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, TRUE, &done);
if(!result) {
conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
@@ -3166,7 +3157,7 @@ static CURLcode ftp_connect(struct Curl_easy *data,
if(conn->handler->flags & PROTOPT_SSL) {
/* BLOCKING */
- result = Curl_ssl_connect(data, conn, FIRSTSOCKET);
+ result = Curl_cfilter_connect(data, conn, FIRSTSOCKET, TRUE, done);
if(result)
return result;
conn->bits.ftp_use_control_ssl = TRUE;
@@ -3309,14 +3300,6 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
}
}
- if(conn->ssl[SECONDARYSOCKET].use) {
- /* The secondary socket is using SSL so we must close down that part
- first before we close the socket for real */
- Curl_ssl_close(data, conn, SECONDARYSOCKET);
-
- /* Note that we keep "use" set to TRUE since that (next) connection is
- still requested to use SSL */
- }
close_secondarysocket(data, conn);
}
@@ -3571,22 +3554,10 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
struct FTP *ftp = NULL;
/* if the second connection isn't done yet, wait for it */
- if(!conn->bits.tcpconnect[SECONDARYSOCKET]) {
- if(Curl_connect_ongoing(conn)) {
- /* As we're in TUNNEL_CONNECT state now, we know the proxy name and port
- aren't used so we blank their arguments. */
- result = Curl_proxyCONNECT(data, SECONDARYSOCKET, NULL, 0);
-
- return result;
- }
-
- result = Curl_is_connected(data, conn, SECONDARYSOCKET, &connected);
-
- /* Ready to do more? */
- if(connected) {
- DEBUGF(infof(data, "DO-MORE connected phase starts"));
- }
- else {
+ if(conn->cfilter[SECONDARYSOCKET]) {
+ result = Curl_cfilter_connect(data, conn, SECONDARYSOCKET,
+ FALSE, &connected);
+ if(result || !connected) {
if(result && (ftpc->count1 == 0)) {
*completep = -1; /* go back to DOING please */
/* this is a EPSV connect failing, try PASV instead */
@@ -3596,19 +3567,6 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
}
}
-#ifndef CURL_DISABLE_PROXY
- result = Curl_proxy_connect(data, SECONDARYSOCKET);
- if(result)
- return result;
-
- if(CONNECT_SECONDARYSOCKET_PROXY_SSL())
- return result;
-
- if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
- Curl_connect_ongoing(conn))
- return result;
-#endif
-
/* Curl_proxy_connect might have moved the protocol state */
ftp = data->req.p.ftp;
@@ -3738,7 +3696,6 @@ CURLcode ftp_perform(struct Curl_easy *data,
{
/* this is FTP and no proxy */
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
DEBUGF(infof(data, "DO phase starts"));
@@ -3758,7 +3715,7 @@ CURLcode ftp_perform(struct Curl_easy *data,
/* run the state-machine */
result = ftp_multi_statemach(data, dophase_done);
- *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
+ *connected = Curl_cfilter_is_connected(data, data->conn, SECONDARYSOCKET);
infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
@@ -4374,6 +4331,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
{
char *type;
struct FTP *ftp;
+ CURLcode result = CURLE_OK;
data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1);
if(!ftp)
@@ -4415,7 +4373,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
ftp->downloadsize = 0;
conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
- return CURLE_OK;
+ return result;
}
#endif /* CURL_DISABLE_FTP */