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
This commit is contained in:
Stefan Eissing 2022-11-11 11:45:34 +01:00 committed by Daniel Stenberg
parent 89ee5cfb38
commit dafdb20a26
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
49 changed files with 3325 additions and 1969 deletions

202
lib/ftp.c
View file

@ -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 */