multi: multi_getsock(), check correct socket

- in phase CONNECTING/TUNNELING/PROTOCONNECT, retrieve
   the socket from the connection filters and do not rely
   on `conn->sockfd` being already set by the transfer.
 - this applies to the default behaviour, a protocol handler
   may override this via its callbacks.
 - add a warning message in multi_getsock() when the transfer
   is expected to have something in its pollset, but instead
   it is empty.

Reported-by: saurabhsingh-dev on github
Fixes #13998
Closes #14011
This commit is contained in:
Stefan Eissing 2024-06-25 11:35:48 +02:00 committed by Daniel Stenberg
parent 8e3e3921e6
commit def99d8507
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2

View File

@ -1024,12 +1024,14 @@ void Curl_attach_connection(struct Curl_easy *data,
static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks) static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
{ {
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
(void)socks; curl_socket_t sockfd;
/* Not using `conn->sockfd` as `Curl_xfer_setup()` initializes
* that *after* the connect. */ if(!conn)
if(conn && conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD) { return GETSOCK_BLANK;
sockfd = Curl_conn_get_socket(data, FIRSTSOCKET);
if(sockfd != CURL_SOCKET_BAD) {
/* Default is to wait to something from the server */ /* Default is to wait to something from the server */
socks[0] = conn->sock[FIRSTSOCKET]; socks[0] = sockfd;
return GETSOCK_READSOCK(0); return GETSOCK_READSOCK(0);
} }
return GETSOCK_BLANK; return GETSOCK_BLANK;
@ -1038,11 +1040,16 @@ static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks) static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks)
{ {
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
if(conn && conn->handler->proto_getsock) curl_socket_t sockfd;
if(!conn)
return GETSOCK_BLANK;
if(conn->handler->proto_getsock)
return conn->handler->proto_getsock(data, conn, socks); return conn->handler->proto_getsock(data, conn, socks);
else if(conn && conn->sockfd != CURL_SOCKET_BAD) { sockfd = Curl_conn_get_socket(data, FIRSTSOCKET);
if(sockfd != CURL_SOCKET_BAD) {
/* Default is to wait to something from the server */ /* Default is to wait to something from the server */
socks[0] = conn->sockfd; socks[0] = sockfd;
return GETSOCK_READSOCK(0); return GETSOCK_READSOCK(0);
} }
return GETSOCK_BLANK; return GETSOCK_BLANK;
@ -1051,9 +1058,11 @@ static int protocol_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks) static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks)
{ {
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
if(conn && conn->handler->domore_getsock) if(!conn)
return GETSOCK_BLANK;
if(conn->handler->domore_getsock)
return conn->handler->domore_getsock(data, conn, socks); return conn->handler->domore_getsock(data, conn, socks);
else if(conn && conn->sockfd != CURL_SOCKET_BAD) { else if(conn->sockfd != CURL_SOCKET_BAD) {
/* Default is that we want to send something to the server */ /* Default is that we want to send something to the server */
socks[0] = conn->sockfd; socks[0] = conn->sockfd;
return GETSOCK_WRITESOCK(0); return GETSOCK_WRITESOCK(0);
@ -1064,9 +1073,11 @@ static int domore_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks) static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks)
{ {
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
if(conn && conn->handler->doing_getsock) if(!conn)
return GETSOCK_BLANK;
if(conn->handler->doing_getsock)
return conn->handler->doing_getsock(data, conn, socks); return conn->handler->doing_getsock(data, conn, socks);
else if(conn && conn->sockfd != CURL_SOCKET_BAD) { else if(conn->sockfd != CURL_SOCKET_BAD) {
/* Default is that we want to send something to the server */ /* Default is that we want to send something to the server */
socks[0] = conn->sockfd; socks[0] = conn->sockfd;
return GETSOCK_WRITESOCK(0); return GETSOCK_WRITESOCK(0);
@ -1077,7 +1088,6 @@ static int doing_getsock(struct Curl_easy *data, curl_socket_t *socks)
static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock) static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
{ {
struct connectdata *conn = data->conn; struct connectdata *conn = data->conn;
if(!conn) if(!conn)
return GETSOCK_BLANK; return GETSOCK_BLANK;
else if(conn->handler->perform_getsock) else if(conn->handler->perform_getsock)
@ -1114,6 +1124,7 @@ static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
static void multi_getsock(struct Curl_easy *data, static void multi_getsock(struct Curl_easy *data,
struct easy_pollset *ps) struct easy_pollset *ps)
{ {
bool expect_sockets = TRUE;
/* The no connection case can happen when this is called from /* The no connection case can happen when this is called from
curl_multi_remove_handle() => singlesocket() => multi_getsock(). curl_multi_remove_handle() => singlesocket() => multi_getsock().
*/ */
@ -1127,6 +1138,7 @@ static void multi_getsock(struct Curl_easy *data,
case MSTATE_SETUP: case MSTATE_SETUP:
case MSTATE_CONNECT: case MSTATE_CONNECT:
/* nothing to poll for yet */ /* nothing to poll for yet */
expect_sockets = FALSE;
break; break;
case MSTATE_RESOLVING: case MSTATE_RESOLVING:
@ -1165,19 +1177,26 @@ static void multi_getsock(struct Curl_easy *data,
case MSTATE_RATELIMITING: case MSTATE_RATELIMITING:
/* we need to let time pass, ignore socket(s) */ /* we need to let time pass, ignore socket(s) */
expect_sockets = FALSE;
break; break;
case MSTATE_DONE: case MSTATE_DONE:
case MSTATE_COMPLETED: case MSTATE_COMPLETED:
case MSTATE_MSGSENT: case MSTATE_MSGSENT:
/* nothing more to poll for */ /* nothing more to poll for */
expect_sockets = FALSE;
break; break;
default: default:
failf(data, "multi_getsock: unexpected multi state %d", data->mstate); failf(data, "multi_getsock: unexpected multi state %d", data->mstate);
DEBUGASSERT(0); DEBUGASSERT(0);
expect_sockets = FALSE;
break; break;
} }
if(expect_sockets && !ps->num && !Curl_xfer_is_blocked(data)) {
infof(data, "WARNING: no socket in pollset, transfer may stall!");
}
} }
CURLMcode curl_multi_fdset(struct Curl_multi *multi, CURLMcode curl_multi_fdset(struct Curl_multi *multi,