diff options
author | Stefan Eissing <stefan@eissing.org> | 2022-11-11 11:45:34 +0100 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2022-11-11 15:17:51 +0100 |
commit | dafdb20a26d0c890e83dea61a104b75408481ebd (patch) | |
tree | 40824f46de18cb7b7b47fb06a3be624c9c06961b /lib/ftp.c | |
parent | 89ee5cfb38b22f9ff68c34aa55ca2c242be90826 (diff) | |
download | curl-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.c | 202 |
1 files changed, 80 insertions, 122 deletions
@@ -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 */ |