summaryrefslogtreecommitdiff
path: root/Utilities/cmcurl/lib/connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/connect.c')
-rw-r--r--Utilities/cmcurl/lib/connect.c459
1 files changed, 331 insertions, 128 deletions
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index ac007c61b0..af0413836b 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -48,13 +48,6 @@
#include <arpa/inet.h>
#endif
-#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
-#include <sys/filio.h>
-#endif
-#ifdef NETWARE
-#undef in_addr_t
-#define in_addr_t unsigned long
-#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
@@ -64,6 +57,7 @@
#include "sendf.h"
#include "if2ip.h"
#include "strerror.h"
+#include "cfilters.h"
#include "connect.h"
#include "select.h"
#include "url.h" /* for Curl_safefree() */
@@ -79,7 +73,6 @@
#include "share.h"
#include "version_win32.h"
#include "quic.h"
-#include "socks.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -237,10 +230,9 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
return timeout_ms;
}
-static CURLcode bindlocal(struct Curl_easy *data,
+static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t sockfd, int af, unsigned int scope)
{
- struct connectdata *conn = data->conn;
struct Curl_sockaddr_storage sa;
struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
@@ -398,18 +390,23 @@ static CURLcode bindlocal(struct Curl_easy *data,
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
char *scope_ptr = strchr(myhost, '%');
if(scope_ptr)
- *(scope_ptr++) = 0;
+ *(scope_ptr++) = '\0';
#endif
if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
si6->sin6_family = AF_INET6;
si6->sin6_port = htons(port);
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
- if(scope_ptr)
+ if(scope_ptr) {
/* The "myhost" string either comes from Curl_if2ip or from
Curl_printable_address. The latter returns only numeric scope
IDs and the former returns none at all. So the scope ID, if
present, is known to be numeric */
- si6->sin6_scope_id = atoi(scope_ptr);
+ unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
+ if(scope_id > UINT_MAX)
+ return CURLE_UNSUPPORTED_PROTOCOL;
+
+ si6->sin6_scope_id = (unsigned int)scope_id;
+ }
#endif
}
sizeof_sa = sizeof(struct sockaddr_in6);
@@ -771,123 +768,45 @@ void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
Curl_persistconninfo(data, conn, local_ip, local_port);
}
-/* After a TCP connection to the proxy has been verified, this function does
- the next magic steps. If 'done' isn't set TRUE, it is not done yet and
- must be called again.
-
- Note: this function's sub-functions call failf()
-
-*/
-static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex,
- bool *done)
-{
- CURLcode result = CURLE_OK;
-#ifndef CURL_DISABLE_PROXY
- CURLproxycode pxresult = CURLPX_OK;
- struct connectdata *conn = data->conn;
- if(conn->bits.socksproxy) {
- /* for the secondary socket (FTP), use the "connect to host"
- * but ignore the "connect to port" (use the secondary port)
- */
- const char * const host =
- conn->bits.httpproxy ?
- conn->http_proxy.host.name :
- conn->bits.conn_to_host ?
- conn->conn_to_host.name :
- sockindex == SECONDARYSOCKET ?
- conn->secondaryhostname : conn->host.name;
- const int port =
- conn->bits.httpproxy ? (int)conn->http_proxy.port :
- sockindex == SECONDARYSOCKET ? conn->secondary_port :
- conn->bits.conn_to_port ? conn->conn_to_port :
- conn->remote_port;
- switch(conn->socks_proxy.proxytype) {
- case CURLPROXY_SOCKS5:
- case CURLPROXY_SOCKS5_HOSTNAME:
- pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
- host, port, sockindex, data, done);
- break;
-
- case CURLPROXY_SOCKS4:
- case CURLPROXY_SOCKS4A:
- pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
- data, done);
- break;
-
- default:
- failf(data, "unknown proxytype option given");
- result = CURLE_COULDNT_CONNECT;
- } /* switch proxytype */
- if(pxresult) {
- result = CURLE_PROXY;
- data->info.pxcode = pxresult;
- }
- }
- else
-#else
- (void)data;
- (void)sockindex;
-#endif /* CURL_DISABLE_PROXY */
- *done = TRUE; /* no SOCKS proxy, so consider us connected */
-
- return result;
-}
-
/*
- * post_SOCKS() is called after a successful connect to the peer, which
- * *could* be a SOCKS proxy
+ * post_connect() is called after a successful connect to the peer
*/
-static void post_SOCKS(struct Curl_easy *data,
+static void post_connect(struct Curl_easy *data,
struct connectdata *conn,
- int sockindex,
- bool *connected)
+ int sockindex)
{
- conn->bits.tcpconnect[sockindex] = TRUE;
-
- *connected = TRUE;
- if(sockindex == FIRSTSOCKET)
- Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
Curl_updateconninfo(data, conn, conn->sock[sockindex]);
Curl_verboseconnect(data, conn);
data->info.numconnects++; /* to track the number of connections made */
}
/*
- * Curl_is_connected() checks if the socket has connected.
+ * is_connected() checks if the socket has connected.
*/
-
-CURLcode Curl_is_connected(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex,
- bool *connected)
+static CURLcode is_connected(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex,
+ bool *connected)
{
CURLcode result = CURLE_OK;
timediff_t allow;
int error = 0;
struct curltime now;
int rc = 0;
- unsigned int i;
+ int i;
DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
*connected = FALSE; /* a very negative world view is best */
- if(conn->bits.tcpconnect[sockindex]) {
- /* we are connected already! */
- *connected = TRUE;
- return CURLE_OK;
- }
-
now = Curl_now();
- if(SOCKS_STATE(conn->cnnct.state)) {
- /* still doing SOCKS */
- result = connect_SOCKS(data, sockindex, connected);
- if(!result && *connected)
- post_SOCKS(data, conn, sockindex, connected);
- return result;
- }
-
+ /* Check if any of the conn->tempsock we use for establishing connections
+ * succeeded and, if so, close any ongoing other ones.
+ * Transfer the successful conn->tempsock to conn->sock[sockindex]
+ * and set conn->tempsock to CURL_SOCKET_BAD.
+ * If transport is QUIC, we need to shutdown the ongoing 'other'
+ * connect attempts in a QUIC appropriate way. */
for(i = 0; i<2; i++) {
const int other = i ^ 1;
if(conn->tempsock[i] == CURL_SOCKET_BAD)
@@ -901,7 +820,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
conn->sock[sockindex] = conn->tempsock[i];
conn->ip_addr = conn->tempaddr[i];
conn->tempsock[i] = CURL_SOCKET_BAD;
- post_SOCKS(data, conn, sockindex, connected);
+ post_connect(data, conn, sockindex);
connkeep(conn, "HTTP/3 default");
if(conn->tempsock[other] != CURL_SOCKET_BAD)
Curl_quic_disconnect(data, conn, other);
@@ -963,14 +882,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
conn->tempsock[other] = CURL_SOCKET_BAD;
}
- /* see if we need to kick off any SOCKS proxy magic once we
- connected */
- result = connect_SOCKS(data, sockindex, connected);
- if(result || !*connected)
- return result;
-
- post_SOCKS(data, conn, sockindex, connected);
-
+ *connected = TRUE;
return CURLE_OK;
}
}
@@ -1207,7 +1119,7 @@ static CURLcode singleipconnect(struct Curl_easy *data,
return result;
/* store remote address and port used in this connection attempt */
- if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
+ if(!Curl_addr2string(&addr.sa_addr, addr.addrlen,
ipaddress, &port)) {
/* malformed address or bug in inet_ntop, try next address */
failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
@@ -1261,8 +1173,8 @@ static CURLcode singleipconnect(struct Curl_easy *data,
|| addr.family == AF_INET6
#endif
) {
- result = bindlocal(data, sockfd, addr.family,
- Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
+ result = bindlocal(data, conn, sockfd, addr.family,
+ Curl_ipv6_scope(&addr.sa_addr));
if(result) {
Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
if(result == CURLE_UNSUPPORTED_PROTOCOL) {
@@ -1521,12 +1433,13 @@ curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
/*
* Check if a connection seems to be alive.
*/
-bool Curl_connalive(struct connectdata *conn)
+bool Curl_connalive(struct Curl_easy *data, struct connectdata *conn)
{
+ (void)data;
/* First determine if ssl */
- if(conn->ssl[FIRSTSOCKET].use) {
+ if(Curl_conn_is_ssl(data, FIRSTSOCKET)) {
/* use the SSL context */
- if(!Curl_ssl_check_cxn(conn))
+ if(!Curl_ssl_check_cxn(data, conn))
return false; /* FIN received */
}
/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
@@ -1714,16 +1627,306 @@ void Curl_conncontrol(struct connectdata *conn,
}
}
-/* Data received can be cached at various levels, so check them all here. */
-bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
+typedef enum {
+ SCFST_INIT,
+ SCFST_WAITING,
+ SCFST_DONE
+} cf_connect_state;
+
+struct socket_cf_ctx {
+ const struct Curl_dns_entry *remotehost;
+ cf_connect_state state;
+};
+
+static int socket_cf_get_select_socks(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ curl_socket_t *socks)
+{
+ struct connectdata *conn = cf->conn;
+ int i, s, rc = GETSOCK_BLANK;
+
+ (void)data;
+ if(cf->connected) {
+ return rc;
+ }
+
+ for(i = s = 0; i<2; i++) {
+ if(conn->tempsock[i] != CURL_SOCKET_BAD) {
+ socks[s] = conn->tempsock[i];
+ rc |= GETSOCK_WRITESOCK(s);
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC)
+ /* when connecting QUIC, we want to read the socket too */
+ rc |= GETSOCK_READSOCK(s);
+#endif
+ s++;
+ }
+ }
+
+ return rc;
+}
+
+static CURLcode socket_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ struct connectdata *conn = cf->conn;
+ int sockindex = cf->sockindex;
+ struct socket_cf_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ (void)blocking;
+ DEBUGASSERT(ctx);
+ *done = FALSE;
+ switch(ctx->state) {
+ case SCFST_INIT:
+ DEBUGASSERT(CURL_SOCKET_BAD == conn->sock[sockindex]);
+ DEBUGASSERT(!cf->connected);
+ result = Curl_connecthost(data, conn, ctx->remotehost);
+ if(!result)
+ ctx->state = SCFST_WAITING;
+ break;
+ case SCFST_WAITING:
+ result = is_connected(data, conn, sockindex, done);
+ if(!result && *done) {
+ Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
+ if(Curl_conn_is_ssl(data, FIRSTSOCKET) ||
+ (conn->handler->protocol & PROTO_FAMILY_SSH))
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
+ post_connect(data, conn, sockindex);
+ ctx->state = SCFST_DONE;
+ cf->connected = TRUE;
+ }
+ break;
+ case SCFST_DONE:
+ *done = TRUE;
+ break;
+ }
+ return result;
+}
+
+static CURLcode socket_cf_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost)
+{
+ struct socket_cf_ctx *ctx = cf->ctx;
+
+ (void)data;
+ DEBUGASSERT(ctx);
+ if(ctx->remotehost != remotehost) {
+ if(ctx->remotehost) {
+ /* switching dns entry? TODO: reset? */
+ }
+ ctx->remotehost = remotehost;
+ }
+ DEBUGF(infof(data, CFMSG(cf, "setup(remotehost=%s)"),
+ cf->conn->hostname_resolve));
+ return CURLE_OK;
+}
+
+static void socket_cf_close(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ int sockindex = cf->sockindex;
+ struct socket_cf_ctx *ctx = cf->ctx;
+
+ DEBUGASSERT(ctx);
+ /* close possibly still open sockets */
+ if(CURL_SOCKET_BAD != cf->conn->sock[sockindex]) {
+ Curl_closesocket(data, cf->conn, cf->conn->sock[sockindex]);
+ cf->conn->sock[sockindex] = CURL_SOCKET_BAD;
+ }
+ if(CURL_SOCKET_BAD != cf->conn->tempsock[sockindex]) {
+ Curl_closesocket(data, cf->conn, cf->conn->tempsock[sockindex]);
+ cf->conn->tempsock[sockindex] = CURL_SOCKET_BAD;
+ }
+ cf->connected = FALSE;
+ ctx->state = SCFST_INIT;
+}
+
+static void socket_cf_get_host(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport)
+{
+ (void)data;
+ *phost = cf->conn->host.name;
+ *pdisplay_host = cf->conn->host.dispname;
+ *pport = cf->conn->port;
+}
+
+static bool socket_cf_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
int readable;
+ (void)data;
+ DEBUGASSERT(cf);
+
+ readable = SOCKET_READABLE(cf->conn->sock[cf->sockindex], 0);
+ return (readable > 0 && (readable & CURL_CSELECT_IN));
+}
+
+static ssize_t socket_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+ const void *buf, size_t len, CURLcode *err)
+{
+ ssize_t nwritten;
+ nwritten = Curl_send_plain(data, cf->sockindex, buf, len, err);
+ return nwritten;
+}
+
+static ssize_t socket_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+ char *buf, size_t len, CURLcode *err)
+{
+ ssize_t nread;
+ nread = Curl_recv_plain(data, cf->sockindex, buf, len, err);
+ return nread;
+}
+
+static void socket_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct socket_cf_ctx *state = cf->ctx;
+
+ (void)data;
+ if(cf->connected) {
+ socket_cf_close(cf, data);
+ }
+ /* release any resources held in state */
+ Curl_safefree(state);
+}
+
+static const struct Curl_cftype cft_socket = {
+ "SOCKET",
+ CF_TYPE_IP_CONNECT,
+ socket_cf_destroy,
+ socket_cf_setup,
+ socket_cf_connect,
+ socket_cf_close,
+ socket_cf_get_host,
+ socket_cf_get_select_socks,
+ socket_cf_data_pending,
+ socket_cf_send,
+ socket_cf_recv,
+ Curl_cf_def_attach_data,
+ Curl_cf_def_detach_data,
+};
+
+CURLcode Curl_conn_socket_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode result;
+ struct Curl_cfilter *cf = NULL;
+ struct socket_cf_ctx *scf_ctx = NULL;
+
+ /* Need to be first */
DEBUGASSERT(conn);
+ DEBUGASSERT(!conn->cfilter[sockindex]);
+ scf_ctx = calloc(sizeof(*scf_ctx), 1);
+ if(!scf_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ result = Curl_cf_create(&cf, &cft_socket, scf_ctx);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
- if(Curl_ssl_data_pending(conn, sockindex) ||
- Curl_recv_has_postponed_data(conn, sockindex))
- return true;
+out:
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(scf_ctx);
+ }
+ return result;
+}
- readable = SOCKET_READABLE(conn->sock[sockindex], 0);
- return (readable > 0 && (readable & CURL_CSELECT_IN));
+static CURLcode socket_accept_cf_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool blocking, bool *done)
+{
+ /* we start accepted, if we ever close, we cannot go on */
+ (void)data;
+ (void)blocking;
+ if(cf->connected) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
+static CURLcode socket_accept_cf_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct Curl_dns_entry *remotehost)
+{
+ /* we start accepted, if we ever close, we cannot go on */
+ (void)data;
+ (void)remotehost;
+ if(cf->connected) {
+ return CURLE_OK;
+ }
+ return CURLE_FAILED_INIT;
+}
+
+static const struct Curl_cftype cft_socket_accept = {
+ "SOCKET-ACCEPT",
+ CF_TYPE_IP_CONNECT,
+ socket_cf_destroy,
+ socket_accept_cf_setup,
+ socket_accept_cf_connect,
+ socket_cf_close,
+ socket_cf_get_host, /* TODO: not accurate */
+ Curl_cf_def_get_select_socks,
+ socket_cf_data_pending,
+ socket_cf_send,
+ socket_cf_recv,
+ Curl_cf_def_attach_data,
+ Curl_cf_def_detach_data,
+};
+
+CURLcode Curl_conn_socket_accepted_set(struct Curl_easy *data,
+ struct connectdata *conn,
+ int sockindex, curl_socket_t *s)
+{
+ CURLcode result;
+ struct Curl_cfilter *cf = NULL;
+ struct socket_cf_ctx *scf_ctx = NULL;
+
+ cf = conn->cfilter[sockindex];
+ if(cf && cf->cft == &cft_socket_accept) {
+ /* already an accept filter installed, just replace the socket */
+ scf_ctx = cf->ctx;
+ result = CURLE_OK;
+ }
+ else {
+ /* replace any existing */
+ Curl_conn_cf_discard_all(data, conn, sockindex);
+ scf_ctx = calloc(sizeof(*scf_ctx), 1);
+ if(!scf_ctx) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ result = Curl_cf_create(&cf, &cft_socket_accept, scf_ctx);
+ if(result)
+ goto out;
+ Curl_conn_cf_add(data, conn, sockindex, cf);
+ }
+
+ /* close any existing socket and replace */
+ Curl_closesocket(data, conn, conn->sock[sockindex]);
+ conn->sock[sockindex] = *s;
+ conn->bits.sock_accepted = TRUE;
+ cf->connected = TRUE;
+ scf_ctx->state = SCFST_DONE;
+
+out:
+ if(result) {
+ Curl_safefree(cf);
+ Curl_safefree(scf_ctx);
+ }
+ return result;
}